diff --git a/sql/swad.sql b/sql/swad.sql index 054ef93f1..c7fc69d91 100644 --- a/sql/swad.sql +++ b/sql/swad.sql @@ -1385,6 +1385,7 @@ CREATE TABLE IF NOT EXISTS tst_exams ( EndTime DATETIME NOT NULL, NumQsts INT NOT NULL DEFAULT 0, NumQstsNotBlank INT NOT NULL DEFAULT 0, + Sent ENUM('N','Y') NOT NULL DEFAULT 'N', AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N', Score DOUBLE PRECISION NOT NULL DEFAULT 0, UNIQUE INDEX(ExaCod), @@ -1416,15 +1417,6 @@ CREATE TABLE IF NOT EXISTS tst_questions ( INDEX(CrsCod,EditTime), INDEX(MedCod)); -- --- Table tst_status: stores the status of tests for each session --- -CREATE TABLE IF NOT EXISTS tst_status ( - SessionId CHAR(43) NOT NULL, - CrsCod INT NOT NULL, - NumTst INT NOT NULL, - Status TINYINT NOT NULL, - UNIQUE INDEX(SessionId,CrsCod,NumTst)); --- -- Table tst_tags: stores the tags of test questions -- CREATE TABLE IF NOT EXISTS tst_tags ( diff --git a/swad_action.c b/swad_action.c index 45ffa648e..b58681a88 100644 --- a/swad_action.c +++ b/swad_action.c @@ -642,7 +642,7 @@ const struct Act_Actions Act_Actions[Act_NUM_ACTIONS] = [ActDowAssPrj ] = {1734,-1,TabUnk,ActSeePrj ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_DOWNLD_FILE,Brw_DownloadFile ,NULL ,NULL}, [ActSeeTst ] = { 29,-1,TabUnk,ActReqTst ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Tst_ShowNewTest ,NULL}, - [ActReqAssTst ] = {1837,-1,TabUnk,ActReqTst ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Tst_RequestAssessTest ,NULL}, + [ActReqAssTst ] = {1837,-1,TabUnk,ActReqTst ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Tst_ReceiveTestDraft ,NULL}, [ActAssTst ] = { 98,-1,TabUnk,ActReqTst ,0x238,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,NULL ,Tst_AssessTest ,NULL}, [ActEdiTstQst ] = { 104,-1,TabUnk,ActReqTst ,0x220,0x200, 0, 0, 0, 0, 0,Act_CONT_NORM,Act_BRW_1ST_TAB,Dat_SetIniEndDates ,Tst_RequestEditTests ,NULL}, diff --git a/swad_changelog.h b/swad_changelog.h index fffebb499..9d64ceffd 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -497,7 +497,7 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - * En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 19.188.2 (2020-04-16)" +#define Log_PLATFORM_VERSION "SWAD 19.189 (2020-04-16)" #define CSS_FILE "swad19.187.css" #define JS_FILE "swad19.172.1.js" /* @@ -547,7 +547,16 @@ Funci // TODO: Miguel Damas: al principio de los exámenes tendría que poner cuánto resta cada pregunta // TODO: Oresti Baños: cambiar ojos por candados en descriptores para prohibir/permitir y dejar los ojos para poder elegir descriptores // TODO: Integrar pull requests con traducciones del alemán del usuario eruedin en GitHub -// TODO: Cambiar icono de inicio a "house-user.svg", notificaciones nuevas con "bell-on.svg" +// TODO: Cambiar icono notificaciones nuevas con "bell-on.svg" + + Version 19.189: Apr 16, 2020 Fixed security issue in test exams. + Removed table with test status. (287719 lines) + 3/4 changes necessary in database: +ALTER TABLE tst_exams ADD COLUMN Sent ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER NumQstsNotBlank; +UPDATE tst_exams SET Sent='Y'; +DROP TABLE IF EXISTS tst_status; +Only if you use MyISAM: +ALTER TABLE buildings ENGINE=MyISAM; Version 19.188.2: Apr 16, 2020 Changed some icons. (287754 lines) Copy the following icons to icon public directory: diff --git a/swad_database.c b/swad_database.c index 5aa5fb082..d2733d801 100644 --- a/swad_database.c +++ b/swad_database.c @@ -2876,10 +2876,11 @@ mysql> DESCRIBE tst_exams; | EndTime | datetime | NO | | NULL | | | NumQsts | int(11) | NO | | 0 | | | NumQstsNotBlank | int(11) | NO | | 0 | | +| Sent | enum('N','Y') | NO | | N | | | AllowTeachers | enum('N','Y') | NO | | N | | | Score | double | NO | | 0 | | +-----------------+---------------+------+-----+---------+----------------+ -9 rows in set (0.00 sec) +10 rows in set (0.00 sec) */ DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_exams (" "ExaCod INT NOT NULL AUTO_INCREMENT," @@ -2889,6 +2890,7 @@ mysql> DESCRIBE tst_exams; "EndTime DATETIME NOT NULL," "NumQsts INT NOT NULL DEFAULT 0," "NumQstsNotBlank INT NOT NULL DEFAULT 0," + "Sent ENUM('N','Y') NOT NULL DEFAULT 'N'," "AllowTeachers ENUM('N','Y') NOT NULL DEFAULT 'N'," "Score DOUBLE PRECISION NOT NULL DEFAULT 0," "UNIQUE INDEX(ExaCod)," @@ -2948,26 +2950,6 @@ mysql> DESCRIBE tst_questions; "INDEX(CrsCod,EditTime)," "INDEX(MedCod))"); - /***** Table tst_status *****/ -/* -mysql> DESCRIBE tst_status; -+-----------+------------+------+-----+---------+-------+ -| Field | Type | Null | Key | Default | Extra | -+-----------+------------+------+-----+---------+-------+ -| SessionId | char(43) | NO | PRI | NULL | | -| CrsCod | int(11) | NO | PRI | NULL | | -| NumTst | int(11) | NO | PRI | NULL | | -| Status | tinyint(4) | NO | | NULL | | -+-----------+------------+------+-----+---------+-------+ -4 rows in set (0.00 sec) -*/ - DB_CreateTable ("CREATE TABLE IF NOT EXISTS tst_status (" - "SessionId CHAR(43) NOT NULL," - "CrsCod INT NOT NULL," - "NumTst INT NOT NULL," - "Status TINYINT NOT NULL," - "UNIQUE INDEX(SessionId,CrsCod,NumTst))"); - /***** Table tst_tags *****/ /* mysql> DESCRIBE tst_tags; diff --git a/swad_game.c b/swad_game.c index 7e0bbc687..0776c07f2 100644 --- a/swad_game.c +++ b/swad_game.c @@ -61,7 +61,7 @@ extern struct Globals Gbl; #define Gam_MAX_ANSWERS_PER_QUESTION 10 -#define Gam_MAX_SELECTED_QUESTIONS 1000 +#define Gam_MAX_SELECTED_QUESTIONS 10000 #define Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS (Gam_MAX_SELECTED_QUESTIONS * (Cns_MAX_DECIMAL_DIGITS_LONG + 1)) /* Score range [0...max.score] @@ -2150,6 +2150,9 @@ void Gam_AddTstQuestionsToGame (void) long QstCod; unsigned MaxQstInd; + /***** Reset games *****/ + Gam_ResetGames (&Games); + /***** Get parameters *****/ if ((Game.GamCod = Gam_GetParams (&Games)) <= 0) Lay_ShowErrorAndExit ("Code of game is missing."); @@ -2214,7 +2217,7 @@ static void Gam_AllocateListSelectedQuestions (struct Gam_Games *Games) if (!Games->ListQuestions) { if ((Games->ListQuestions = (char *) malloc (Gam_MAX_BYTES_LIST_SELECTED_QUESTIONS + 1)) == NULL) - Lay_NotEnoughMemoryExit ();; + Lay_NotEnoughMemoryExit (); Games->ListQuestions[0] = '\0'; } } @@ -2429,6 +2432,9 @@ void Gam_MoveDownQst (void) unsigned QstIndBottom; unsigned MaxQstInd; // 0 if no questions + /***** Reset games *****/ + Gam_ResetGames (&Games); + /***** Get parameters *****/ if ((Game.GamCod = Gam_GetParams (&Games)) <= 0) Lay_ShowErrorAndExit ("Code of game is missing."); diff --git a/swad_room.c b/swad_room.c index 4421ac7e5..509b385cf 100644 --- a/swad_room.c +++ b/swad_room.c @@ -176,6 +176,11 @@ void Roo_SeeRooms (void) HTM_Int (Rooms.Lst[NumRoom].Floor); HTM_TD_End (); + /* Type */ + HTM_TD_Begin ("class=\"DAT LM %s\"",Gbl.ColorRows[RowEvenOdd]); + HTM_Txt (Rooms.Lst[NumRoom].ShrtName); + HTM_TD_End (); + /* Short name */ HTM_TD_Begin ("class=\"DAT LM %s\"",Gbl.ColorRows[RowEvenOdd]); HTM_Txt (Rooms.Lst[NumRoom].ShrtName); @@ -341,9 +346,10 @@ void Roo_GetListRooms (struct Roo_Rooms *Rooms, { [Roo_ORDER_BY_BUILDING ] = "buildings.ShortName,rooms.Floor,rooms.ShortName", [Roo_ORDER_BY_FLOOR ] = "rooms.Floor,buildings.ShortName,rooms.ShortName", + [Roo_ORDER_BY_TYPE ] = "rooms.Floor,buildings.ShortName,rooms.ShortName", [Roo_ORDER_BY_SHRT_NAME] = "rooms.ShortName,rooms.FullName", [Roo_ORDER_BY_FULL_NAME] = "rooms.FullName,rooms.ShortName", - [Roo_ORDER_BY_CAPACITY ] = "rooms.Capacity DESC,rooms.ShortName", + [Roo_ORDER_BY_CAPACITY ] = "rooms.Capacity DESC,buildings.ShortName,rooms.Floor,rooms.ShortName", }; MYSQL_RES *mysql_res; MYSQL_ROW row; @@ -602,6 +608,15 @@ static void Roo_ListRoomsForEdition (const struct Bld_Buildings *Buildings, Frm_EndForm (); HTM_TD_End (); + /* Room type */ + HTM_TD_Begin ("class=\"LM\""); + Frm_StartForm (ActRenRooSho); + Roo_PutParamRooCod (Room->RooCod); + HTM_INPUT_TEXT ("ShortName",Roo_MAX_CHARS_SHRT_NAME,Room->ShrtName,true, + "size=\"10\" class=\"INPUT_SHORT_NAME\""); + Frm_EndForm (); + HTM_TD_End (); + /* Room short name */ HTM_TD_Begin ("class=\"LM\""); Frm_StartForm (ActRenRooSho); @@ -1112,6 +1127,12 @@ static void Roo_PutFormToCreateRoom (const struct Bld_Buildings *Buildings) "class=\"INPUT_LONG\""); HTM_TD_End (); + /***** Room type *****/ + HTM_TD_Begin ("class=\"LM\""); + HTM_INPUT_TEXT ("ShortName",Roo_MAX_CHARS_SHRT_NAME,Roo_EditingRoom->ShrtName,false, + "size=\"10\" class=\"INPUT_SHORT_NAME\" required=\"required\""); + HTM_TD_End (); + /***** Room short name *****/ HTM_TD_Begin ("class=\"LM\""); HTM_INPUT_TEXT ("ShortName",Roo_MAX_CHARS_SHRT_NAME,Roo_EditingRoom->ShrtName,false, @@ -1149,6 +1170,7 @@ static void Roo_PutHeadRooms (void) extern const char *Txt_Code; extern const char *Txt_Building; extern const char *Txt_Floor; + extern const char *Txt_Type; extern const char *Txt_Short_name; extern const char *Txt_Full_name; extern const char *Txt_Capacity_OF_A_ROOM; @@ -1159,6 +1181,7 @@ static void Roo_PutHeadRooms (void) HTM_TH (1,1,"RM",Txt_Code); HTM_TH (1,1,"LM",Txt_Building); HTM_TH (1,1,"LM",Txt_Floor); + HTM_TH (1,1,"LM",Txt_Type); HTM_TH (1,1,"LM",Txt_Short_name); HTM_TH (1,1,"LM",Txt_Full_name); HTM_TH (1,1,"LM",Txt_Capacity_OF_A_ROOM); diff --git a/swad_room.h b/swad_room.h index accff9d2b..19e9b8cdf 100644 --- a/swad_room.h +++ b/swad_room.h @@ -85,14 +85,15 @@ struct Roo_Room // (maximum people who fit in the room) }; -#define Roo_NUM_ORDERS 5 +#define Roo_NUM_ORDERS 6 typedef enum { Roo_ORDER_BY_BUILDING = 0, Roo_ORDER_BY_FLOOR = 1, - Roo_ORDER_BY_SHRT_NAME = 2, - Roo_ORDER_BY_FULL_NAME = 3, - Roo_ORDER_BY_CAPACITY = 4, + Roo_ORDER_BY_TYPE = 2, + Roo_ORDER_BY_SHRT_NAME = 3, + Roo_ORDER_BY_FULL_NAME = 4, + Roo_ORDER_BY_CAPACITY = 5, } Roo_Order_t; #define Roo_ORDER_DEFAULT Roo_ORDER_BY_BUILDING diff --git a/swad_test.c b/swad_test.c index 429032dba..d2a9c79e5 100644 --- a/swad_test.c +++ b/swad_test.c @@ -102,14 +102,6 @@ static const char *Tst_StrAnswerTypesDB[Tst_NUM_ANS_TYPES] = /******************************* Private types *******************************/ /*****************************************************************************/ -#define Tst_NUM_STATUS 2 -typedef enum - { - Tst_STATUS_SHOWN_BUT_NOT_ASSESSED = 0, - Tst_STATUS_ASSESSED = 1, - Tst_STATUS_ERROR = 2, - } Tst_Status_t; - #define Tst_NUM_REQUEST_OR_CONFIRM 2 typedef enum { @@ -143,8 +135,6 @@ static void Tst_PutCheckBoxAllowTeachers (bool AllowTeachers); static void Tst_GetAnswersFromForm (struct TstExa_Exam *Exam); static bool Tst_CheckIfNextTstAllowed (void); -static void Tst_SetTstStatus (unsigned NumTst,Tst_Status_t TstStatus); -static Tst_Status_t Tst_GetTstStatus (unsigned NumTst); static unsigned Tst_GetNumExamsGeneratedByMe (void); static void Tst_ShowTestExamToFillIt (struct TstExa_Exam *Exam, unsigned NumExamsGeneratedByMe, @@ -482,9 +472,6 @@ void Tst_ShowNewTest (void) /***** Show test exam to be answered *****/ Tst_ShowTestExamToFillIt (&Exam,NumExamsGeneratedByMe,Tst_REQUEST); - /***** Set test status *****/ - Tst_SetTstStatus (NumExamsGeneratedByMe,Tst_STATUS_SHOWN_BUT_NOT_ASSESSED); - /***** Update date-time of my next allowed access to test *****/ if (Gbl.Usrs.Me.Role.Logged == Rol_STD) Tst_UpdateLastAccTst (Test.NumQsts); @@ -525,13 +512,12 @@ static void Tst_PutCheckBoxAllowTeachers (bool AllowTeachers) } /*****************************************************************************/ -/********************* Request the assessment of a test **********************/ +/** Receive the draft of a test exam already (total or partially) answered ***/ /*****************************************************************************/ -void Tst_RequestAssessTest (void) +void Tst_ReceiveTestDraft (void) { extern const char *Txt_The_test_X_has_already_been_assessed_previously; - extern const char *Txt_There_was_an_error_in_assessing_the_test_X; unsigned NumTst; struct TstExa_Exam Exam; @@ -547,37 +533,32 @@ void Tst_RequestAssessTest (void) /* Get number of this test from form */ NumTst = Tst_GetParamNumTst (); + /***** Get test exam from database *****/ + TstExa_GetExamDataByExaCod (&Exam); + /****** Get test status in database for this session-course-num.test *****/ - switch (Tst_GetTstStatus (NumTst)) + if (Exam.Sent) + Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously, + NumTst); + else // Exam not yet sent { - case Tst_STATUS_SHOWN_BUT_NOT_ASSESSED: - /***** Get test exam from database *****/ - TstExa_GetExamDataByExaCod (&Exam); - TstExa_GetExamQuestionsFromDB (&Exam); + /***** Get test exam questions from database *****/ + TstExa_GetExamQuestionsFromDB (&Exam); - /***** Get answers from form to assess a test *****/ - Tst_GetAnswersFromForm (&Exam); + /***** Get answers from form to assess a test *****/ + Tst_GetAnswersFromForm (&Exam); - /***** Update test exam in database *****/ - TstExa_ComputeScoresAndStoreExamQuestions (&Exam, - false); // Don't update question score - TstExa_UpdateExamInDB (&Exam); + /***** Update test exam in database *****/ + TstExa_ComputeScoresAndStoreExamQuestions (&Exam, + false); // Don't update question score + TstExa_UpdateExamInDB (&Exam); - /***** Show question and button to send the test *****/ - /* Start alert */ - Ale_ShowAlert (Ale_WARNING,"Por favor, revise sus respuestas antes de enviar el examen:"); // TODO: Need translation!!! + /***** Show question and button to send the test *****/ + /* Start alert */ + Ale_ShowAlert (Ale_WARNING,"Por favor, revise sus respuestas antes de enviar el examen:"); // TODO: Need translation!!! - /* Show the same test exam to be answered */ - Tst_ShowTestExamToFillIt (&Exam,NumTst,Tst_CONFIRM); - break; - case Tst_STATUS_ASSESSED: - Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously, - NumTst); - break; - case Tst_STATUS_ERROR: - Ale_ShowAlert (Ale_WARNING,Txt_There_was_an_error_in_assessing_the_test_X, - NumTst); - break; + /* Show the same test exam to be answered */ + Tst_ShowTestExamToFillIt (&Exam,NumTst,Tst_CONFIRM); } } @@ -593,7 +574,6 @@ void Tst_AssessTest (void) extern const char *Txt_Score; extern const char *Txt_Grade; extern const char *Txt_The_test_X_has_already_been_assessed_previously; - extern const char *Txt_There_was_an_error_in_assessing_the_test_X; unsigned NumTst; struct TstExa_Exam Exam; @@ -609,73 +589,66 @@ void Tst_AssessTest (void) /* Get number of this test from form */ NumTst = Tst_GetParamNumTst (); + /***** Get test exam from database *****/ + TstExa_GetExamDataByExaCod (&Exam); + /****** Get test status in database for this session-course-num.test *****/ - switch (Tst_GetTstStatus (NumTst)) + if (Exam.Sent) + Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously, + NumTst); + else // Exam not yet sent { - case Tst_STATUS_SHOWN_BUT_NOT_ASSESSED: - /***** Get test exam from database *****/ - TstExa_GetExamDataByExaCod (&Exam); - TstExa_GetExamQuestionsFromDB (&Exam); + /***** Get test exam questions from database *****/ + TstExa_GetExamQuestionsFromDB (&Exam); - /***** Get answers from form to assess a test *****/ - Tst_GetAnswersFromForm (&Exam); + /***** Get answers from form to assess a test *****/ + Tst_GetAnswersFromForm (&Exam); - /***** Get if test exam will be visible by teachers *****/ - Exam.AllowTeachers = Par_GetParToBool ("AllowTchs"); + /***** Get if test exam will be visible by teachers *****/ + Exam.Sent = true; // The exam has been finished and sent by student + Exam.AllowTeachers = Par_GetParToBool ("AllowTchs"); - /***** Update test exam in database *****/ - TstExa_ComputeScoresAndStoreExamQuestions (&Exam, - Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score? - TstExa_UpdateExamInDB (&Exam); + /***** Update test exam in database *****/ + TstExa_ComputeScoresAndStoreExamQuestions (&Exam, + Gbl.Usrs.Me.Role.Logged == Rol_STD); // Update question score? + TstExa_UpdateExamInDB (&Exam); - /***** Begin box *****/ - Box_BoxBegin (NULL,Txt_Test_result, - NULL,NULL, - Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE); - Lay_WriteHeaderClassPhoto (false,false, - Gbl.Hierarchy.Ins.InsCod, - Gbl.Hierarchy.Deg.DegCod, - Gbl.Hierarchy.Crs.CrsCod); + /***** Begin box *****/ + Box_BoxBegin (NULL,Txt_Test_result, + NULL,NULL, + Hlp_ASSESSMENT_Tests,Box_NOT_CLOSABLE); + Lay_WriteHeaderClassPhoto (false,false, + Gbl.Hierarchy.Ins.InsCod, + Gbl.Hierarchy.Deg.DegCod, + Gbl.Hierarchy.Crs.CrsCod); - /***** Header *****/ - if (Gbl.Usrs.Me.IBelongToCurrentCrs) - { - HTM_DIV_Begin ("class=\"TEST_SUBTITLE\""); - HTM_TxtF (Txt_Test_No_X_that_you_make_in_this_course,NumTst); - HTM_DIV_End (); - } + /***** Header *****/ + if (Gbl.Usrs.Me.IBelongToCurrentCrs) + { + HTM_DIV_Begin ("class=\"TEST_SUBTITLE\""); + HTM_TxtF (Txt_Test_No_X_that_you_make_in_this_course,NumTst); + HTM_DIV_End (); + } - /***** Write answers and solutions *****/ - TstExa_ShowExamAfterAssess (&Exam); + /***** Write answers and solutions *****/ + TstExa_ShowExamAfterAssess (&Exam); - /***** Write total score and grade *****/ - if (TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ())) - { - HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\""); - HTM_TxtColonNBSP (Txt_Score); - HTM_Double2Decimals (Exam.Score); - HTM_BR (); - HTM_TxtColonNBSP (Txt_Grade); - TstExa_ComputeAndShowGrade (Exam.NumQsts, - Exam.Score, - TstExa_SCORE_MAX); - HTM_DIV_End (); - } + /***** Write total score and grade *****/ + if (TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ())) + { + HTM_DIV_Begin ("class=\"DAT_N_BOLD CM\""); + HTM_TxtColonNBSP (Txt_Score); + HTM_Double2Decimals (Exam.Score); + HTM_BR (); + HTM_TxtColonNBSP (Txt_Grade); + TstExa_ComputeAndShowGrade (Exam.NumQsts, + Exam.Score, + TstExa_SCORE_MAX); + HTM_DIV_End (); + } - /***** End box *****/ - Box_BoxEnd (); - - /***** Set test status *****/ - Tst_SetTstStatus (NumTst,Tst_STATUS_ASSESSED); - break; - case Tst_STATUS_ASSESSED: - Ale_ShowAlert (Ale_WARNING,Txt_The_test_X_has_already_been_assessed_previously, - NumTst); - break; - case Tst_STATUS_ERROR: - Ale_ShowAlert (Ale_WARNING,Txt_There_was_an_error_in_assessing_the_test_X, - NumTst); - break; + /***** End box *****/ + Box_BoxEnd (); } } @@ -764,62 +737,6 @@ static bool Tst_CheckIfNextTstAllowed (void) return true; } -/*****************************************************************************/ -/****************************** Update test status ***************************/ -/*****************************************************************************/ - -static void Tst_SetTstStatus (unsigned NumTst,Tst_Status_t TstStatus) - { - /***** Delete old status from expired sessions *****/ - DB_QueryDELETE ("can not remove old status of tests", - "DELETE FROM tst_status" - " WHERE SessionId NOT IN" - " (SELECT SessionId FROM sessions)"); - - /***** Update database *****/ - DB_QueryREPLACE ("can not update status of test", - "REPLACE INTO tst_status" - " (SessionId,CrsCod,NumTst,Status)" - " VALUES" - " ('%s',%ld,%u,%u)", - Gbl.Session.Id,Gbl.Hierarchy.Crs.CrsCod, - NumTst,(unsigned) TstStatus); - } - -/*****************************************************************************/ -/****************************** Update test status ***************************/ -/*****************************************************************************/ - -static Tst_Status_t Tst_GetTstStatus (unsigned NumTst) - { - MYSQL_RES *mysql_res; - MYSQL_ROW row; - unsigned UnsignedNum; - Tst_Status_t TstStatus = Tst_STATUS_ERROR; - - /***** Get status of test from database *****/ - if (DB_QuerySELECT (&mysql_res,"can not get status of test", - "SELECT Status" // row[0] - " FROM tst_status" - " WHERE SessionId='%s'" - " AND CrsCod=%ld" - " AND NumTst=%u", - Gbl.Session.Id,Gbl.Hierarchy.Crs.CrsCod,NumTst) == 1) - { - /* Get number of hits */ - row = mysql_fetch_row (mysql_res); - if (row[0]) - if (sscanf (row[0],"%u",&UnsignedNum) == 1) - if (UnsignedNum < Tst_NUM_STATUS) - TstStatus = (Tst_Status_t) UnsignedNum; - } - - /***** Free structure that stores the query result *****/ - DB_FreeMySQLResult (&mysql_res); - - return TstStatus; - } - /*****************************************************************************/ /***************** Get number of test exams generated by me ******************/ /*****************************************************************************/ @@ -5968,16 +5885,11 @@ static void Tst_InsertAnswersIntoDB (struct Tst_Question *Question) } /*****************************************************************************/ -/******************* Remove all test exams made in a course ******************/ +/******************* Remove all test questions in a course *******************/ /*****************************************************************************/ void Tst_RemoveCrsTests (long CrsCod) { - /***** Remove tests status in the course *****/ - DB_QueryDELETE ("can not remove status of tests of a course", - "DELETE FROM tst_status WHERE CrsCod=%ld", - CrsCod); - /***** Remove test configuration of the course *****/ DB_QueryDELETE ("can not remove configuration of tests of a course", "DELETE FROM tst_config WHERE CrsCod=%ld", diff --git a/swad_test.h b/swad_test.h index 42002c3c6..0daa0cc42 100644 --- a/swad_test.h +++ b/swad_test.h @@ -155,7 +155,7 @@ struct Tst_Stats void Tst_RequestTest (void); void Tst_ShowNewTest (void); -void Tst_RequestAssessTest (void); +void Tst_ReceiveTestDraft (void); void Tst_AssessTest (void); void Tst_ShowTagList (unsigned NumTags,MYSQL_RES *mysql_res); diff --git a/swad_test_exam.c b/swad_test_exam.c index 522c41a1d..a4158c5c5 100644 --- a/swad_test_exam.c +++ b/swad_test_exam.c @@ -56,14 +56,6 @@ /******************************* Private types *******************************/ /*****************************************************************************/ -#define Tst_NUM_STATUS 2 -typedef enum - { - Tst_STATUS_SHOWN_BUT_NOT_ASSESSED = 0, - Tst_STATUS_ASSESSED = 1, - Tst_STATUS_ERROR = 2, - } Tst_Status_t; - /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ @@ -78,6 +70,8 @@ extern struct Globals Gbl; /***************************** Private prototypes ****************************/ /*****************************************************************************/ +static void TstExa_ResetExamExceptExaCod (struct TstExa_Exam *Exam); + static void TstExa_WriteQstAndAnsExam (struct UsrData *UsrDat, struct TstExa_Exam *Exam, unsigned NumQst, @@ -164,12 +158,18 @@ static void TstExa_ShowTagsPresentInAnExam (long ExaCod); void TstExa_ResetExam (struct TstExa_Exam *Exam) { - Exam->ExaCod = -1L; + Exam->ExaCod = -1L; + TstExa_ResetExamExceptExaCod (Exam); + } + +static void TstExa_ResetExamExceptExaCod (struct TstExa_Exam *Exam) + { Exam->TimeUTC[Dat_START_TIME] = Exam->TimeUTC[Dat_END_TIME ] = (time_t) 0; Exam->NumQsts = Exam->NumQstsNotBlank = 0; - Exam->AllowTeachers = false; + Exam->Sent = false; // After creating an exam, it's not sent + Exam->AllowTeachers = false; // Teachers can't seen the exam if student don't allow it Exam->Score = 0.0; } @@ -183,9 +183,11 @@ void TstExa_CreateExamInDB (struct TstExa_Exam *Exam) Exam->ExaCod = DB_QueryINSERTandReturnCode ("can not create new test exam", "INSERT INTO tst_exams" - " (CrsCod,UsrCod,StartTime,EndTime,NumQsts,AllowTeachers,Score)" + " (CrsCod,UsrCod,StartTime,EndTime,NumQsts," + "Sent,AllowTeachers,Score)" " VALUES" - " (%ld,%ld,NOW(),NOW(),%u,'N',0)", + " (%ld,%ld,NOW(),NOW(),%u," + "'N','N',0)", Gbl.Hierarchy.Crs.CrsCod, Gbl.Usrs.Me.UsrDat.UsrCod, Exam->NumQsts); @@ -203,11 +205,14 @@ void TstExa_UpdateExamInDB (const struct TstExa_Exam *Exam) "UPDATE tst_exams" " SET EndTime=NOW()," "NumQstsNotBlank=%u," + "Sent='%c'," "AllowTeachers='%c'," "Score='%.15lg'" " WHERE ExaCod=%ld" " AND CrsCod=%ld AND UsrCod=%ld", // Extra checks Exam->NumQstsNotBlank, + Exam->Sent ? 'Y' : + 'N', Exam->AllowTeachers ? 'Y' : 'N', Exam->Score, @@ -396,7 +401,6 @@ void TstExa_ComputeScoresAndStoreExamQuestions (struct TstExa_Exam *Exam, if (Exam->Questions[NumQst].AnswerIsNotBlank) Exam->NumQstsNotBlank++; - /* Update the number of hits and the score of this question in tests database */ if (UpdateQstScore) Tst_UpdateQstScoreInDB (Exam,NumQst); @@ -1634,8 +1638,12 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) double TotalScoreOfAllTests = 0.0; unsigned NumExamsVisibleByTchs = 0; bool ItsMe = Usr_ItsMe (UsrDat->UsrCod); - bool ICanViewTest; - bool ICanViewScore; + struct + { + bool NumQsts; + bool Score; + bool Exam; + } ICanView; char *ClassDat; /***** Make database query *****/ @@ -1651,8 +1659,9 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) "UNIX_TIMESTAMP(EndTime)," // row[2] "NumQsts," // row[3] "NumQstsNotBlank," // row[4] - "AllowTeachers," // row[5] - "Score" // row[6] + "Sent," // row[5] + "AllowTeachers," // row[6] + "Score" // row[7] " FROM tst_exams" " WHERE CrsCod=%ld AND UsrCod=%ld" " AND EndTime>=FROM_UNIXTIME(%ld)" @@ -1681,34 +1690,41 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) if ((Exam.ExaCod = Str_ConvertStrCodToLongCod (row[0])) < 0) Lay_ShowErrorAndExit ("Wrong code of test exam."); - /* Get if teachers are allowed to see this test exams (row[5]) */ - Exam.AllowTeachers = (row[5][0] == 'Y'); + /* Get if exam has been sent (row[5]) */ + Exam.Sent = (row[5][0] == 'Y'); + + /* Get if teachers are allowed to see this test exam (row[6]) */ + Exam.AllowTeachers = (row[6][0] == 'Y'); ClassDat = Exam.AllowTeachers ? "DAT" : - "DAT_LIGHT"; + "DAT_LIGHT"; switch (Gbl.Usrs.Me.Role.Logged) { case Rol_STD: - ICanViewTest = ItsMe; - ICanViewScore = ItsMe && - TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()); + ICanView.NumQsts = Exam.Sent && ItsMe; + ICanView.Score = Exam.Sent && ItsMe && + TstVis_IsVisibleTotalScore (TstCfg_GetConfigVisibility ()); + ICanView.Exam = Exam.Sent && ItsMe; break; case Rol_NET: case Rol_TCH: case Rol_DEG_ADM: case Rol_CTR_ADM: case Rol_INS_ADM: - ICanViewTest = - ICanViewScore = ItsMe || - Exam.AllowTeachers; + ICanView.NumQsts = Exam.Sent; // If the exam has been sent, + // teachers can see the number of questions + ICanView.Score = + ICanView.Exam = Exam.Sent && (ItsMe || Exam.AllowTeachers); break; case Rol_SYS_ADM: - ICanViewTest = - ICanViewScore = true; + ICanView.NumQsts = + ICanView.Score = + ICanView.Exam = true; break; default: - ICanViewTest = - ICanViewScore = false; + ICanView.NumQsts = + ICanView.Score = + ICanView.Exam = false; break; } @@ -1746,9 +1762,9 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) if (Exam.AllowTeachers) NumTotalQstsNotBlank += Exam.NumQstsNotBlank; - /* Get score (row[6]) */ + /* Get score (row[7]) */ Str_SetDecimalPointToUS (); // To get the decimal point as a dot - if (sscanf (row[6],"%lf",&Exam.Score) != 1) + if (sscanf (row[7],"%lf",&Exam.Score) != 1) Exam.Score = 0.0; Str_SetDecimalPointToLocal (); // Return to local system if (Exam.AllowTeachers) @@ -1756,25 +1772,25 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) /* Write number of questions */ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd); - if (ICanViewTest) + if (ICanView.NumQsts) HTM_Unsigned (Exam.NumQsts); HTM_TD_End (); /* Write number of questions not blank */ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd); - if (ICanViewTest) + if (ICanView.NumQsts) HTM_Unsigned (Exam.NumQstsNotBlank); HTM_TD_End (); /* Write score */ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd); - if (ICanViewScore) + if (ICanView.Score) HTM_Double2Decimals (Exam.Score); HTM_TD_End (); /* Write average score per question */ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd); - if (ICanViewScore) + if (ICanView.Score) HTM_Double2Decimals (Exam.NumQsts ? Exam.Score / (double) Exam.NumQsts : 0.0); @@ -1782,15 +1798,15 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) /* Write grade */ HTM_TD_Begin ("class=\"%s RT COLOR%u\"",ClassDat,Gbl.RowEvenOdd); - if (ICanViewScore) + if (ICanView.Score) TstExa_ComputeAndShowGrade (Exam.NumQsts, - Exam.Score, - TstExa_SCORE_MAX); + Exam.Score, + TstExa_SCORE_MAX); HTM_TD_End (); /* Link to show this test exam */ HTM_TD_Begin ("class=\"RT COLOR%u\"",Gbl.RowEvenOdd); - if (ICanViewTest) + if (ICanView.Exam) { Frm_StartForm (Gbl.Action.Act == ActSeeMyTstRes ? ActSeeOneTstResMe : ActSeeOneTstResOth); @@ -1807,8 +1823,8 @@ static void TstExa_ShowExams (struct UsrData *UsrDat) /***** Write totals for this user *****/ TstExa_ShowExamsSummaryRow (ItsMe,NumExamsVisibleByTchs, - NumTotalQsts,NumTotalQstsNotBlank, - TotalScoreOfAllTests); + NumTotalQsts,NumTotalQstsNotBlank, + TotalScoreOfAllTests); } else { @@ -2242,8 +2258,9 @@ void TstExa_GetExamDataByExaCod (struct TstExa_Exam *Exam) "UNIX_TIMESTAMP(EndTime)," // row[2] "NumQsts," // row[3] "NumQstsNotBlank," // row[4] - "AllowTeachers," // row[5] - "Score" // row[6] + "Sent," // row[5] + "AllowTeachers," // row[6] + "Score" // row[7] " FROM tst_exams" " WHERE ExaCod=%ld AND CrsCod=%ld", Exam->ExaCod, @@ -2266,24 +2283,20 @@ void TstExa_GetExamDataByExaCod (struct TstExa_Exam *Exam) if (sscanf (row[4],"%u",&Exam->NumQstsNotBlank) != 1) Exam->NumQstsNotBlank = 0; - /* Get if teachers are allowed to see this test exam (row[5]) */ - Exam->AllowTeachers = (row[5][0] == 'Y'); + /* Get if exam has been sent (row[5]) */ + Exam->Sent = (row[5][0] == 'Y'); - /* Get score (row[6]) */ + /* Get if teachers are allowed to see this test exam (row[6]) */ + Exam->AllowTeachers = (row[6][0] == 'Y'); + + /* Get score (row[7]) */ Str_SetDecimalPointToUS (); // To get the decimal point as a dot - if (sscanf (row[6],"%lf",&Exam->Score) != 1) + if (sscanf (row[7],"%lf",&Exam->Score) != 1) Exam->Score = 0.0; Str_SetDecimalPointToLocal (); // Return to local system } else - { - Exam->TimeUTC[Dat_START_TIME] = - Exam->TimeUTC[Dat_END_TIME ] = 0; - Exam->NumQsts = 0; - Exam->NumQstsNotBlank = 0; - Exam->AllowTeachers = false; - Exam->Score = 0.0; - } + TstExa_ResetExamExceptExaCod (Exam); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); diff --git a/swad_test_exam.h b/swad_test_exam.h index 11f1eaeaf..969210e0a 100644 --- a/swad_test_exam.h +++ b/swad_test_exam.h @@ -51,6 +51,8 @@ struct TstExa_Exam time_t TimeUTC[Dat_NUM_START_END_TIME]; unsigned NumQsts; // Number of questions unsigned NumQstsNotBlank; // Number of questions not blank + bool Sent; // This test exam has been sent or not? + // "Sent" means that user has clicked "Send" button after finishing bool AllowTeachers; // Are teachers allowed to see this test exam? double Score; // Total score of the test exam struct diff --git a/swad_text.c b/swad_text.c index 84e4a6d25..eabd89b54 100644 --- a/swad_text.c +++ b/swad_text.c @@ -38201,6 +38201,27 @@ const char *Txt_ROOMS_HELP_ORDER[Roo_NUM_ORDERS] = "Sortuj według piętrze" #elif L==9 // pt "Classificar por andar" +#endif + , + [Roo_ORDER_BY_TYPE] = +#if L==1 // ca + "Ordenar per tipus" +#elif L==2 // de + "Nach Art sortieren" +#elif L==3 // en + "Sort by type" +#elif L==4 // es + "Ordenar por tipo" +#elif L==5 // fr + "Trier par type" +#elif L==6 // gn + "Ordenar por tipo" // Okoteve traducción +#elif L==7 // it + "Ordina per tipo" +#elif L==8 // pl + "Sortuj według typ" +#elif L==9 // pt + "Classificar por tipo" #endif , [Roo_ORDER_BY_SHRT_NAME] = @@ -38309,6 +38330,27 @@ const char *Txt_ROOMS_ORDER[Roo_NUM_ORDERS] = "Piętrze" #elif L==9 // pt "Andar" +#endif + , + [Roo_ORDER_BY_TYPE] = +#if L==1 // ca + "Tipus" +#elif L==2 // de + "Art" +#elif L==3 // en + "Type" +#elif L==4 // es + "Tipo" +#elif L==5 // fr + "Type" +#elif L==6 // gn + "Teko" +#elif L==7 // it + "Tipo" +#elif L==8 // pl + "Typ" +#elif L==9 // pt + "Tipo" #endif , [Roo_ORDER_BY_SHRT_NAME] = @@ -50306,27 +50348,6 @@ const char *Txt_There_was_a_problem_sending_an_email_automatically = "Ocorreu um problema ao enviar um email automaticamente."; #endif -const char *Txt_There_was_an_error_in_assessing_the_test_X = // Warning: it is very important to include %u in the following sentences -#if L==1 // ca - "Se ha producido un error al evaluar el test %u."; // Necessita traduccio -#elif L==2 // de - "There was an error in assessing the test %u."; // Need Übersetzung -#elif L==3 // en - "There was an error in assessing the test %u."; -#elif L==4 // es - "Se ha producido un error al evaluar el test %u."; -#elif L==5 // fr - "There was an error in assessing the test %u."; // Besoin de traduction -#elif L==6 // gn - "Se ha producido un error al evaluar el test %u."; // Okoteve traducción -#elif L==7 // it - "C'è stato un errore nel valutare il test %u."; -#elif L==8 // pl - "There was an error in assessing the test %u."; // Potrzebujesz tlumaczenie -#elif L==9 // pt - "Houve um erro ao avaliar o teste %u."; -#endif - const char *Txt_This_game_has_no_questions = #if L==1 // ca "Aquest joc no té preguntes.";