From 1a1d0196b58c1e358f69d4e371630cb18d0f8271 Mon Sep 17 00:00:00 2001 From: acanas Date: Mon, 22 May 2023 14:34:35 +0200 Subject: [PATCH] Version 22.117: May 22, 2023 Teachers can allow comments in a survey question. --- sql/swad.sql | 1 + swad_changelog.h | 6 +- swad_database.c | 22 +-- swad_survey.c | 368 +++++++++++++++++++++++------------------ swad_survey.h | 14 ++ swad_survey_database.c | 54 +++--- swad_survey_database.h | 8 +- swad_text.c | 48 +++++- 8 files changed, 315 insertions(+), 206 deletions(-) diff --git a/sql/swad.sql b/sql/swad.sql index b6b975c4..f2dfc714 100644 --- a/sql/swad.sql +++ b/sql/swad.sql @@ -1428,6 +1428,7 @@ CREATE TABLE IF NOT EXISTS svy_questions ( SvyCod INT NOT NULL, QstInd INT NOT NULL DEFAULT 0, AnsType ENUM('unique_choice','multiple_choice') NOT NULL, + CommentsAllowed ENUM('N','Y') NOT NULL DEFAULT 'N', Stem TEXT NOT NULL, UNIQUE INDEX(QstCod), INDEX(SvyCod)); diff --git a/swad_changelog.h b/swad_changelog.h index 2a0a4ae9..7f87df62 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -629,10 +629,14 @@ TODO: Emilce Barrera Mesa: Podr TODO: Emilce Barrera Mesa: Mis estudiantes presentan muchas dificultades a la hora de poner la foto porque la plataforma es muy exigente respecto al fondo de la imagen. */ -#define Log_PLATFORM_VERSION "SWAD 22.116.1 (2023-05-21)" +#define Log_PLATFORM_VERSION "SWAD 22.117 (2023-05-22)" #define CSS_FILE "swad22.107.36.css" #define JS_FILE "swad22.49.js" /* + Version 22.117: May 22, 2023 Teachers can allow comments in a survey question. (337196 lines) + 1 change necessary in database: +ALTER TABLE svy_questions ADD COLUMN CommentsAllowed ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER AnsType; + Version 22.116.1: May 21, 2023 Rubrics only availables for superusers. (337098 lines) Version 22.116: May 20, 2023 Changes in rubric scores. (337093 lines) Version 22.115: May 19, 2023 Generalization of rubric scores. (337010 lines) diff --git a/swad_database.c b/swad_database.c index da9cdb8c..96b5026e 100644 --- a/swad_database.c +++ b/swad_database.c @@ -3058,22 +3058,24 @@ mysql> DESCRIBE svy_groups; /***** Table svy_questions *****/ /* mysql> DESCRIBE svy_questions; -+---------+-----------------------------------------+------+-----+---------+----------------+ -| Field | Type | Null | Key | Default | Extra | -+---------+-----------------------------------------+------+-----+---------+----------------+ -| QstCod | int(11) | NO | PRI | NULL | auto_increment | -| SvyCod | int(11) | NO | MUL | NULL | | -| QstInd | int(11) | NO | | 0 | | -| AnsType | enum('unique_choice','multiple_choice') | NO | | NULL | | -| Stem | text | NO | | NULL | | -+---------+-----------------------------------------+------+-----+---------+----------------+ -5 rows in set (0.00 sec) ++-----------------+-----------------------------------------+------+-----+---------+----------------+ +| Field | Type | Null | Key | Default | Extra | ++-----------------+-----------------------------------------+------+-----+---------+----------------+ +| QstCod | int | NO | PRI | NULL | auto_increment | +| SvyCod | int | NO | MUL | NULL | | +| QstInd | int | NO | | 0 | | +| AnsType | enum('unique_choice','multiple_choice') | NO | | NULL | | +| CommentsAllowed | enum('N','Y') | NO | | N | | +| Stem | text | NO | | NULL | | ++-----------------+-----------------------------------------+------+-----+---------+----------------+ +6 rows in set (0,00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS svy_questions (" "QstCod INT NOT NULL AUTO_INCREMENT," "SvyCod INT NOT NULL," "QstInd INT NOT NULL DEFAULT 0," "AnsType ENUM ('unique_choice','multiple_choice') NOT NULL," + "CommentsAllowed ENUM('N','Y') NOT NULL DEFAULT 'N'" "Stem TEXT NOT NULL," // Cns_MAX_BYTES_TEXT "UNIQUE INDEX(QstCod)," "INDEX(SvyCod))"); diff --git a/swad_survey.c b/swad_survey.c index 2d46800d..bc303cb6 100644 --- a/swad_survey.c +++ b/swad_survey.c @@ -70,21 +70,6 @@ extern struct Globals Gbl; #define Svy_MAX_BYTES_LIST_ANSWER_TYPES (Svy_NUM_ANS_TYPES * (Cns_MAX_DECIMAL_DIGITS_UINT + 1)) -#define Svy_MAX_ANSWERS_PER_QUESTION 10 - -struct Svy_Question // Must be initialized to 0 before using it - { - long QstCod; - unsigned QstInd; - Svy_AnswerType_t AnswerType; - struct - { - char *Text; - } AnsChoice[Svy_MAX_ANSWERS_PER_QUESTION]; - bool AllAnsTypes; - char ListAnsTypes[Svy_MAX_BYTES_LIST_ANSWER_TYPES + 1]; - }; - /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ @@ -136,6 +121,9 @@ static void Svy_WriteQstStem (const char *Stem); static void Svy_WriteAnswersOfAQst (struct Svy_Survey *Svy, struct Svy_Question *SvyQst, bool PutFormAnswerSurvey); +static void Svy_WriteCommentsOfAQst (struct Svy_Survey *Svy, + struct Svy_Question *SvyQst, + bool PutFormAnswerSurvey); static void Svy_DrawBarNumUsrs (unsigned NumUsrs,unsigned MaxUsrs); static void Svy_PutIconToRemoveOneQst (void *Surveys); @@ -1229,33 +1217,36 @@ void Svy_GetSurveyDataByCod (struct Svy_Survey *Svy) switch (Gbl.Usrs.Me.Role.Logged) { case Rol_STD: - Svy->Status.ICanViewResults = ( Svy->Scope == HieLvl_CRS || + Svy->Status.ICanViewResults = ( Svy->Scope == HieLvl_CRS || + Svy->Scope == HieLvl_DEG || + Svy->Scope == HieLvl_CTR || + Svy->Scope == HieLvl_INS || + Svy->Scope == HieLvl_CTY || + Svy->Scope == HieLvl_SYS) && + ( Svy->NumQsts != 0) && + !Svy->Status.Hidden && + Svy->Status.Open && + Svy->Status.IAmLoggedWithAValidRoleToAnswer && + Svy->Status.IBelongToScope && + Svy->Status.IHaveAnswered; + Svy->Status.ICanViewComments = false; + Svy->Status.ICanEdit = false; + break; + case Rol_NET: + Svy->Status.ICanViewResults = + Svy->Status.ICanViewComments = (Svy->Scope == HieLvl_CRS || Svy->Scope == HieLvl_DEG || Svy->Scope == HieLvl_CTR || Svy->Scope == HieLvl_INS || Svy->Scope == HieLvl_CTY || Svy->Scope == HieLvl_SYS) && - ( Svy->NumQsts != 0) && - !Svy->Status.Hidden && - Svy->Status.Open && - Svy->Status.IAmLoggedWithAValidRoleToAnswer && - Svy->Status.IBelongToScope && - Svy->Status.IHaveAnswered; - Svy->Status.ICanEdit = false; - break; - case Rol_NET: - Svy->Status.ICanViewResults = (Svy->Scope == HieLvl_CRS || - Svy->Scope == HieLvl_DEG || - Svy->Scope == HieLvl_CTR || - Svy->Scope == HieLvl_INS || - Svy->Scope == HieLvl_CTY || - Svy->Scope == HieLvl_SYS) && - Svy->NumQsts != 0 && - !Svy->Status.ICanAnswer; + Svy->NumQsts != 0 && + !Svy->Status.ICanAnswer; Svy->Status.ICanEdit = false; break; case Rol_TCH: - Svy->Status.ICanViewResults = (Svy->Scope == HieLvl_CRS || + Svy->Status.ICanViewResults = + Svy->Status.ICanViewComments = (Svy->Scope == HieLvl_CRS || Svy->Scope == HieLvl_DEG || Svy->Scope == HieLvl_CTR || Svy->Scope == HieLvl_INS || @@ -1266,42 +1257,47 @@ void Svy_GetSurveyDataByCod (struct Svy_Survey *Svy) Svy->Status.ICanEdit = Svy->Scope == HieLvl_CRS; // && Svy->Status.IBelongToScope break; case Rol_DEG_ADM: - Svy->Status.ICanViewResults = (Svy->Scope == HieLvl_DEG || - Svy->Scope == HieLvl_CTR || - Svy->Scope == HieLvl_INS || - Svy->Scope == HieLvl_CTY || - Svy->Scope == HieLvl_SYS) && - (Svy->NumQsts != 0) && - !Svy->Status.ICanAnswer; - Svy->Status.ICanEdit = Svy->Scope == HieLvl_DEG && - Svy->Status.IBelongToScope; + Svy->Status.ICanViewResults = + Svy->Status.ICanViewComments = (Svy->Scope == HieLvl_DEG || + Svy->Scope == HieLvl_CTR || + Svy->Scope == HieLvl_INS || + Svy->Scope == HieLvl_CTY || + Svy->Scope == HieLvl_SYS) && + (Svy->NumQsts != 0) && + !Svy->Status.ICanAnswer; + Svy->Status.ICanEdit = Svy->Scope == HieLvl_DEG && + Svy->Status.IBelongToScope; break; case Rol_CTR_ADM: - Svy->Status.ICanViewResults = (Svy->Scope == HieLvl_CTR || - Svy->Scope == HieLvl_INS || - Svy->Scope == HieLvl_CTY || - Svy->Scope == HieLvl_SYS) && - (Svy->NumQsts != 0) && - !Svy->Status.ICanAnswer; - Svy->Status.ICanEdit = Svy->Scope == HieLvl_CTR && - Svy->Status.IBelongToScope; + Svy->Status.ICanViewResults = + Svy->Status.ICanViewComments = (Svy->Scope == HieLvl_CTR || + Svy->Scope == HieLvl_INS || + Svy->Scope == HieLvl_CTY || + Svy->Scope == HieLvl_SYS) && + (Svy->NumQsts != 0) && + !Svy->Status.ICanAnswer; + Svy->Status.ICanEdit = Svy->Scope == HieLvl_CTR && + Svy->Status.IBelongToScope; break; case Rol_INS_ADM: - Svy->Status.ICanViewResults = (Svy->Scope == HieLvl_INS || - Svy->Scope == HieLvl_CTY || - Svy->Scope == HieLvl_SYS) && - (Svy->NumQsts != 0) && - !Svy->Status.ICanAnswer; - Svy->Status.ICanEdit = Svy->Scope == HieLvl_INS && - Svy->Status.IBelongToScope; + Svy->Status.ICanViewResults = + Svy->Status.ICanViewComments = (Svy->Scope == HieLvl_INS || + Svy->Scope == HieLvl_CTY || + Svy->Scope == HieLvl_SYS) && + (Svy->NumQsts != 0) && + !Svy->Status.ICanAnswer; + Svy->Status.ICanEdit = Svy->Scope == HieLvl_INS && + Svy->Status.IBelongToScope; break; case Rol_SYS_ADM: - Svy->Status.ICanViewResults = (Svy->NumQsts != 0); - Svy->Status.ICanEdit = true; + Svy->Status.ICanViewResults = + Svy->Status.ICanViewComments = (Svy->NumQsts != 0); + Svy->Status.ICanEdit = true; break; default: - Svy->Status.ICanViewResults = false; - Svy->Status.ICanEdit = false; + Svy->Status.ICanViewResults = false; + Svy->Status.ICanViewComments = false; + Svy->Status.ICanEdit = false; break; } } @@ -1324,6 +1320,7 @@ void Svy_GetSurveyDataByCod (struct Svy_Survey *Svy) Svy->Status.IHaveAnswered = false; Svy->Status.ICanAnswer = false; Svy->Status.ICanViewResults = false; + Svy->Status.ICanViewComments = false; Svy->Status.ICanEdit = false; } @@ -1666,6 +1663,7 @@ void Svy_ReqCreatOrEditSvy (void) Surveys.Svy.Status.IHaveAnswered = false; Surveys.Svy.Status.ICanAnswer = false; Surveys.Svy.Status.ICanViewResults = false; + Surveys.Svy.Status.ICanViewComments = false; } else { @@ -2265,6 +2263,9 @@ static void Svy_ShowFormEditOneQst (struct Svy_Surveys *Surveys, extern const char *Txt_Wording; extern const char *Txt_Type; extern const char *Txt_SURVEY_STR_ANSWER_TYPES[Svy_NUM_ANS_TYPES]; + extern const char *Txt_Options; + extern const char *Txt_Comments; + extern const char *Txt_Comments_allowed; extern const char *Txt_Save_changes; extern const char *Txt_Create; MYSQL_RES *mysql_res; @@ -2380,10 +2381,13 @@ static void Svy_ShowFormEditOneQst (struct Svy_Surveys *Surveys, /***** Answers *****/ HTM_TR_Begin (NULL); - HTM_TD_Empty (1); + HTM_TD_Begin ("class=\"RT FORM_IN_%s\"",The_GetSuffix ()); + HTM_TxtColon (Txt_Options); + HTM_TD_End (); - /* Unique or multiple choice answers */ HTM_TD_Begin ("class=\"LT\""); + + /* Unique or multiple choice answers */ HTM_TABLE_BeginPadding (2); for (NumAns = 0; NumAns < Svy_MAX_ANSWERS_PER_QUESTION; @@ -2411,6 +2415,26 @@ static void Svy_ShowFormEditOneQst (struct Svy_Surveys *Surveys, HTM_TR_End (); } HTM_TABLE_End (); + + HTM_TD_End (); + + HTM_TR_End (); + + /***** Comments allowed? *****/ + HTM_TR_Begin (NULL); + + HTM_TD_Begin ("class=\"RT FORM_IN_%s\"",The_GetSuffix ()); + HTM_TxtColon (Txt_Comments); + HTM_TD_End (); + + HTM_TD_Begin ("class=\"LT FORM_IN_%s\"",The_GetSuffix ()); + HTM_LABEL_Begin (NULL); + HTM_INPUT_CHECKBOX ("Comment",HTM_DONT_SUBMIT_ON_CHANGE, + "value=\"Y\"%s", + SvyQst->CommentsAllowed ? " checked=\"checked\"" : + ""); + HTM_Txt (Txt_Comments_allowed); + HTM_LABEL_End (); HTM_TD_End (); HTM_TR_End (); @@ -2449,6 +2473,7 @@ static void Svy_InitQst (struct Svy_Question *SvyQst) NumAns < Svy_MAX_ANSWERS_PER_QUESTION; NumAns++) SvyQst->AnsChoice[NumAns].Text = NULL; + SvyQst->CommentsAllowed = false; } /*****************************************************************************/ @@ -2504,14 +2529,12 @@ void Svy_ReceiveQst (void) { extern const char *Txt_You_must_type_the_question_stem; extern const char *Txt_You_can_not_leave_empty_intermediate_answers; - extern const char *Txt_You_must_type_at_least_the_first_two_answers; extern const char *Txt_The_survey_has_been_modified; struct Svy_Surveys Surveys; struct Svy_Question SvyQst; char Stem[Cns_MAX_BYTES_TEXT + 1]; unsigned NumAns; char AnsStr[8 + 10 + 1]; - unsigned NumLastAns; bool ThereIsEndOfAnswers; bool Error = false; @@ -2550,40 +2573,25 @@ void Svy_ReceiveQst (void) Par_GetParHTML (AnsStr,SvyQst.AnsChoice[NumAns].Text,Svy_MAX_BYTES_ANSWER); } - /***** Make sure that stem and answer are not empty *****/ + /* Get if comments are allowed */ + SvyQst.CommentsAllowed = Par_GetParBool ("Comment"); + + /***** Make sure that stem is not empty *****/ if (Stem[0]) { - if (SvyQst.AnsChoice[0].Text[0]) // If the first answer has been filled - { - for (NumAns = 0, NumLastAns = 0, ThereIsEndOfAnswers = false; - !Error && NumAns < Svy_MAX_ANSWERS_PER_QUESTION; - NumAns++) - if (SvyQst.AnsChoice[NumAns].Text[0]) - { - if (ThereIsEndOfAnswers) - { - Ale_ShowAlert (Ale_WARNING,Txt_You_can_not_leave_empty_intermediate_answers); - Error = true; - } - else - NumLastAns = NumAns; - } - else - ThereIsEndOfAnswers = true; - if (!Error) - { - if (NumLastAns < 1) - { - Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers); - Error = true; - } - } - } - else // If first answer is empty - { - Ale_ShowAlert (Ale_WARNING,Txt_You_must_type_at_least_the_first_two_answers); - Error = true; - } + for (NumAns = 0, ThereIsEndOfAnswers = false; + !Error && NumAns < Svy_MAX_ANSWERS_PER_QUESTION; + NumAns++) + if (SvyQst.AnsChoice[NumAns].Text[0]) + { + if (ThereIsEndOfAnswers) + { + Ale_ShowAlert (Ale_WARNING,Txt_You_can_not_leave_empty_intermediate_answers); + Error = true; + } + } + else + ThereIsEndOfAnswers = true; } else { @@ -2599,12 +2607,11 @@ void Svy_ReceiveQst (void) if (SvyQst.QstCod < 0) // It's a new question { SvyQst.QstInd = Svy_GetNextQuestionIndexInSvy (Surveys.Svy.SvyCod); - SvyQst.QstCod = Svy_DB_CreateQuestion (Surveys.Svy.SvyCod,SvyQst.QstInd, - SvyQst.AnswerType,Stem); + SvyQst.QstCod = Svy_DB_CreateQuestion (Surveys.Svy.SvyCod,&SvyQst,Stem); } else // It's an existing question /* Update question */ - Svy_DB_UpdateQuestion (Surveys.Svy.SvyCod,SvyQst.QstCod,SvyQst.AnswerType,Stem); + Svy_DB_UpdateQuestion (Surveys.Svy.SvyCod,&SvyQst,Stem); /* Insert, update or delete answers in the answers table */ for (NumAns = 0; @@ -2774,6 +2781,9 @@ static void Svy_ListSvyQuestions (struct Svy_Surveys *Surveys) The_GetColorRows ()); Svy_WriteQstStem (Stem); Svy_WriteAnswersOfAQst (&Surveys->Svy,&SvyQst,PutFormAnswerSurvey); + if (SvyQst.CommentsAllowed) + Svy_WriteCommentsOfAQst (&Surveys->Svy,&SvyQst,PutFormAnswerSurvey); + HTM_TD_End (); HTM_TR_End (); @@ -2823,8 +2833,11 @@ static void Svy_GetQstDataFromRow (MYSQL_RES *mysql_res, /***** Get the answer type (row[2]) *****/ SvyQst->AnswerType = Svy_DB_ConvertFromStrAnsTypDBToAnsTyp (row[2]); - /***** Get the stem of the question from the database (row[3]) *****/ - Str_Copy (Stem,row[3],Cns_MAX_BYTES_TEXT); + /***** Get whether the comments are allowed (row[3]) *****/ + SvyQst->CommentsAllowed = (row[3][0] == 'Y'); + + /***** Get the stem of the question from the database (row[4]) *****/ + Str_Copy (Stem,row[4],Cns_MAX_BYTES_TEXT); } /*****************************************************************************/ @@ -2901,84 +2914,113 @@ static void Svy_WriteAnswersOfAQst (struct Svy_Survey *Svy, /* Write one row for each answer */ HTM_TABLE_BeginPadding (5); - for (NumAns = 0; - NumAns < NumAnswers; - NumAns++) - { - row = mysql_fetch_row (mysql_res); + 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) - Err_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) + Err_ShowErrorAndExit ("Error when getting number of users who have marked an answer."); - /* Convert the answer (row[2]), that is in HTML, to rigorous HTML */ - if (!Svy_AllocateTextChoiceAnswer (SvyQst,NumAns)) - /* Abort on error */ - Ale_ShowAlertsAndExit (); + /* Convert the answer (row[2]), that is in HTML, to rigorous HTML */ + if (!Svy_AllocateTextChoiceAnswer (SvyQst,NumAns)) + /* Abort on error */ + Ale_ShowAlertsAndExit (); - Str_Copy (SvyQst->AnsChoice[NumAns].Text,row[2],Svy_MAX_BYTES_ANSWER); - Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, - SvyQst->AnsChoice[NumAns].Text,Svy_MAX_BYTES_ANSWER,false); + Str_Copy (SvyQst->AnsChoice[NumAns].Text,row[2],Svy_MAX_BYTES_ANSWER); + Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML, + SvyQst->AnsChoice[NumAns].Text,Svy_MAX_BYTES_ANSWER,false); - /* Selectors and label with the letter of the answer */ - HTM_TR_Begin (NULL); + /* Selectors and label with the letter of the answer */ + HTM_TR_Begin (NULL); - if (PutFormAnswerSurvey) - { - /* Write selector to choice this answer */ - HTM_TD_Begin ("class=\"LT\""); - snprintf (StrAns,sizeof (StrAns),"Ans%010u", - (unsigned) SvyQst->QstCod); - if (SvyQst->AnswerType == Svy_ANS_UNIQUE_CHOICE) - HTM_INPUT_RADIO (StrAns,HTM_DONT_SUBMIT_ON_CLICK, - "id=\"Ans%010u_%010u\" value=\"%u\"" - " onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u)\"", - (unsigned) SvyQst->QstCod,NumAns,NumAns, - NumAns, - (unsigned) SvyQst->QstCod,NumAnswers); - else // SvyQst->AnswerType == Svy_ANS_MULTIPLE_CHOICE - HTM_INPUT_CHECKBOX (StrAns,HTM_DONT_SUBMIT_ON_CHANGE, - "id=\"Ans%010u_%010u\" value=\"%u\"", - (unsigned) SvyQst->QstCod,NumAns,NumAns, - NumAns); + if (PutFormAnswerSurvey) + { + /* Write selector to choice this answer */ + HTM_TD_Begin ("class=\"LT\""); + snprintf (StrAns,sizeof (StrAns),"Ans%010u", + (unsigned) SvyQst->QstCod); + switch (SvyQst->AnswerType) + { + case Svy_ANS_UNIQUE_CHOICE: + HTM_INPUT_RADIO (StrAns,HTM_DONT_SUBMIT_ON_CLICK, + "id=\"Ans%010u_%u\" value=\"%u\"" + " onclick=\"selectUnselectRadio(this,this.form.Ans%010u,%u)\"", + (unsigned) SvyQst->QstCod,NumAns,NumAns, + (unsigned) SvyQst->QstCod,NumAnswers); + break; + case Svy_ANS_MULTIPLE_CHOICE: + HTM_INPUT_CHECKBOX (StrAns,HTM_DONT_SUBMIT_ON_CHANGE, + "id=\"Ans%010u_%u\" value=\"%u\"", + (unsigned) SvyQst->QstCod,NumAns,NumAns); + break; + default: + break; + } + HTM_TD_End (); + } + + /* Write the number of option */ + HTM_TD_Begin ("class=\"SVY_OPT LT\""); + HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"DAT_%s\"", + (unsigned) SvyQst->QstCod,NumAns, + The_GetSuffix ()); + HTM_TxtF ("%u)",NumAns + 1); + HTM_LABEL_End (); HTM_TD_End (); - } - /* Write the number of option */ - HTM_TD_Begin ("class=\"SVY_OPT LT\""); - HTM_LABEL_Begin ("for=\"Ans%010u_%010u\" class=\"DAT_%s\"", - (unsigned) SvyQst->QstCod,NumAns, - The_GetSuffix ()); - HTM_TxtF ("%u)",NumAns + 1); - HTM_LABEL_End (); - HTM_TD_End (); + /* Write the text of the answer */ + HTM_TD_Begin ("class=\"LT\""); + HTM_LABEL_Begin ("for=\"Ans%010u_%u\" class=\"DAT_%s\"", + (unsigned) SvyQst->QstCod,NumAns, + The_GetSuffix ()); + HTM_Txt (SvyQst->AnsChoice[NumAns].Text); + HTM_LABEL_End (); + HTM_TD_End (); - /* Write the text of the answer */ - HTM_TD_Begin ("class=\"LT\""); - HTM_LABEL_Begin ("for=\"Ans%010u_%010u\" class=\"DAT_%s\"", - (unsigned) SvyQst->QstCod,NumAns, - The_GetSuffix ()); - HTM_Txt (SvyQst->AnsChoice[NumAns].Text); - HTM_LABEL_End (); - HTM_TD_End (); + /* 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); + HTM_TR_End (); - HTM_TR_End (); - - /* Free memory allocated for the answer */ - Svy_FreeTextChoiceAnswer (SvyQst,NumAns); - } + /* Free memory allocated for the answer */ + Svy_FreeTextChoiceAnswer (SvyQst,NumAns); + } HTM_TABLE_End (); } + else + HTM_BR (); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } +/*****************************************************************************/ +/************** Get and write the answers of a survey question ***************/ +/*****************************************************************************/ + +static void Svy_WriteCommentsOfAQst (struct Svy_Survey *Svy, + struct Svy_Question *SvyQst, + bool PutFormAnswerSurvey) + { + if (PutFormAnswerSurvey) + { + HTM_TEXTAREA_Begin ("name=\"Comments\"" + " cols=\"60\" rows=\"4\"" + " class=\"INPUT_%s\"", + The_GetSuffix ()); + HTM_TEXTAREA_End (); + } + else if (Svy->Status.ICanViewComments) + { + HTM_Txt ("Comentarios..."); // TODO + } + } + /*****************************************************************************/ /***************** Draw a bar with the percentage of answers *****************/ /*****************************************************************************/ diff --git a/swad_survey.h b/swad_survey.h index 6e4fd966..79607307 100644 --- a/swad_survey.h +++ b/swad_survey.h @@ -60,6 +60,7 @@ struct Svy_Survey bool IHaveAnswered; // I have already answered this survey bool ICanAnswer; bool ICanViewResults; + bool ICanViewComments; bool ICanEdit; } Status; }; @@ -83,6 +84,19 @@ typedef enum } Svy_AnswerType_t; #define Svy_ANSWER_TYPE_DEFAULT Svy_ANS_UNIQUE_CHOICE +#define Svy_MAX_ANSWERS_PER_QUESTION 10 +struct Svy_Question // Must be initialized to 0 before using it + { + long QstCod; + unsigned QstInd; + Svy_AnswerType_t AnswerType; + struct + { + char *Text; + } AnsChoice[Svy_MAX_ANSWERS_PER_QUESTION]; + bool CommentsAllowed; + }; + /*****************************************************************************/ /***************************** Public prototypes *****************************/ /*****************************************************************************/ diff --git a/swad_survey_database.c b/swad_survey_database.c index 45893fbb..2e252b84 100644 --- a/swad_survey_database.c +++ b/swad_survey_database.c @@ -836,19 +836,21 @@ void Svy_DB_RemoveGrpsSvysIn (HieLvl_Level_t Scope,long Cod) /*************************** Create a new question ***************************/ /*****************************************************************************/ -long Svy_DB_CreateQuestion (long SvyCod,unsigned QstInd, - Svy_AnswerType_t AnswerType, +long Svy_DB_CreateQuestion (long SvyCod, + const struct Svy_Question *SvyQst, const char Stem[Cns_MAX_BYTES_TEXT + 1]) { return DB_QueryINSERTandReturnCode ("can not create question", "INSERT INTO svy_questions" - " (SvyCod,QstInd,AnsType,Stem)" + " (SvyCod,QstInd,AnsType,CommentsAllowed,Stem)" " VALUES" - " (%ld,%u,'%s','%s')", + " (%ld,%u,'%s','%c','%s')", SvyCod, - QstInd, - Svy_DB_StrAnswerTypes[AnswerType], + SvyQst->QstInd, + Svy_DB_StrAnswerTypes[SvyQst->AnswerType], + SvyQst->CommentsAllowed ? 'Y' : + 'N', Stem); } @@ -856,19 +858,22 @@ long Svy_DB_CreateQuestion (long SvyCod,unsigned QstInd, /************************ Create an existing question ************************/ /*****************************************************************************/ -void Svy_DB_UpdateQuestion (long SvyCod,long QstCod, - Svy_AnswerType_t AnswerType, +void Svy_DB_UpdateQuestion (long SvyCod, + const struct Svy_Question *SvyQst, const char Stem[Cns_MAX_BYTES_TEXT + 1]) { DB_QueryUPDATE ("can not update question", "UPDATE svy_questions" " SET Stem='%s'," - "AnsType='%s'" + "AnsType='%s'," + "CommentsAllowed='%c'" " WHERE QstCod=%ld" " AND SvyCod=%ld", // Extra check Stem, - Svy_DB_StrAnswerTypes[AnswerType], - QstCod, + Svy_DB_StrAnswerTypes[SvyQst->AnswerType], + SvyQst->CommentsAllowed ? 'Y' : + 'N', + SvyQst->QstCod, SvyCod); } @@ -924,10 +929,11 @@ unsigned Svy_DB_GetSurveyQsts (MYSQL_RES **mysql_res,long SvyCod) { return (unsigned) DB_QuerySELECT (mysql_res,"can not get data of questions of a survey", - "SELECT QstCod," // row[0] - "QstInd," // row[1] - "AnsType," // row[2] - "Stem" // row[3] + "SELECT QstCod," // row[0] + "QstInd," // row[1] + "AnsType," // row[2] + "CommentsAllowed," // row[3] + "Stem" // row[4] " FROM svy_questions" " WHERE SvyCod=%ld" " ORDER BY QstInd", @@ -942,10 +948,11 @@ unsigned Svy_DB_GetQstDataByCod (MYSQL_RES **mysql_res,long QstCod,long SvyCod) { return (unsigned) DB_QuerySELECT (mysql_res,"can not get a question", - "SELECT QstCod," // row[0] - "QstInd," // row[1] - "AnsType," // row[2] - "Stem" // row[3] + "SELECT QstCod," // row[0] + "QstInd," // row[1] + "AnsType," // row[2] + "CommentsAllowed," // row[3] + "Stem" // row[4] " FROM svy_questions" " WHERE QstCod=%ld" " AND SvyCod=%ld", // Extra check @@ -1110,10 +1117,7 @@ bool Svy_DB_CheckIfAnswerExists (long QstCod,unsigned AnsInd) unsigned Svy_DB_GetAnswersQst (MYSQL_RES **mysql_res,long QstCod) { - unsigned NumAnswers; - - /***** Get answers of a question from database *****/ - NumAnswers = (unsigned) + return (unsigned) DB_QuerySELECT (mysql_res,"can not get answers of a question", "SELECT AnsInd," // row[0] "NumUsrs," // row[1] @@ -1122,10 +1126,6 @@ unsigned Svy_DB_GetAnswersQst (MYSQL_RES **mysql_res,long QstCod) " WHERE QstCod=%ld" " ORDER BY AnsInd", QstCod); - if (!NumAnswers) - Err_WrongAnswerExit (); - - return NumAnswers; } /*****************************************************************************/ diff --git a/swad_survey_database.h b/swad_survey_database.h index 53d783cd..3cc982b0 100644 --- a/swad_survey_database.h +++ b/swad_survey_database.h @@ -73,11 +73,11 @@ void Svy_DB_RemoveGrpsAssociatedToSurvey (long SvyCod); void Svy_DB_RemoveGrpsSvysIn (HieLvl_Level_t Scope,long Cod); //--------------------------- Surveys questions ------------------------------- -long Svy_DB_CreateQuestion (long SvyCod,unsigned QstInd, - Svy_AnswerType_t AnswerType, +long Svy_DB_CreateQuestion (long SvyCod, + const struct Svy_Question *SvyQst, const char Stem[Cns_MAX_BYTES_TEXT + 1]); -void Svy_DB_UpdateQuestion (long SvyCod,long QstCod, - Svy_AnswerType_t AnswerType, +void Svy_DB_UpdateQuestion (long SvyCod, + const struct Svy_Question *SvyQst, const char Stem[Cns_MAX_BYTES_TEXT + 1]); void Svy_DB_ChangeIndexesQsts (long SvyCod,unsigned QstInd); diff --git a/swad_text.c b/swad_text.c index 3f462672..e448ca46 100644 --- a/swad_text.c +++ b/swad_text.c @@ -5429,7 +5429,30 @@ const char *Txt_Comments = #elif L==9 // pt "Comentários"; #elif L==10 // tr - "Comments"; // Çeviri lazim! + "Yorumlar"; +#endif + +const char *Txt_Comments_allowed = +#if L==1 // ca + "Comentaris permesos"; +#elif L==2 // de + "Kommentare erlaubt"; +#elif L==3 // en + "Comments allowed"; +#elif L==4 // es + "Comentarios permitidos"; +#elif L==5 // fr + "Commentaires autorisés"; +#elif L==6 // gn + "Ojeheja umi comentario"; +#elif L==7 // it + "Commenti consentiti"; +#elif L==8 // pl + "Komentarze dozwolone"; +#elif L==9 // pt + "Comentários permitidos"; +#elif L==10 // tr + "Yorumlara izin verilir"; #endif const char *Txt_Configure_projects = @@ -28515,6 +28538,29 @@ const char *Txt_optional = "optional"; // Çeviri lazim! #endif +const char *Txt_Options = +#if L==1 // ca + "Opcions"; +#elif L==2 // de + "Optionen"; +#elif L==3 // en + "Options"; +#elif L==4 // es + "Opciones"; +#elif L==5 // fr + "Choix"; +#elif L==6 // gn + "Opciones"; +#elif L==7 // it + "Opzioni"; +#elif L==8 // pl + "Opcje"; +#elif L==9 // pt + "Opções"; +#elif L==10 // tr + "Seçenekler"; +#endif + const char *Txt_or_you_can_create_a_new_link_inside_the_folder_X = // Warning: it is very important to include %s in the following sentences #if L==1 // ca "…o pot crear un novo enllaç"