From 4e51c2192d128b0e95457d4273bd3a8ee22cf083 Mon Sep 17 00:00:00 2001 From: acanas Date: Thu, 18 Jun 2020 02:06:02 +0200 Subject: [PATCH] Version19.250.3 --- swad_changelog.h | 6 +- swad_exam_print.c | 15 ++-- swad_exam_print.h | 2 +- swad_exam_result.c | 166 +++++++++++++++++++++++++++++---------------- 4 files changed, 119 insertions(+), 70 deletions(-) diff --git a/swad_changelog.h b/swad_changelog.h index d80881d7b..0dd0dc99d 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -556,15 +556,17 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - * En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 19.250.2 (2020-06-17)" +#define Log_PLATFORM_VERSION "SWAD 19.250.3 (2020-06-18)" #define CSS_FILE "swad19.250.css" #define JS_FILE "swad19.246.1.js" /* TODO: Encarnación Hidalgo Tenorio: Antonio, ¿podría @swad_ugr mandar una notificación cuando el alumnado ha mandado su tarea? Se trataría de añadir un par de líneas "Nuevos archivos en actividades", "Nuevos archivos en otros trabajos". TODO: Fix bug: Cuando se pulsa en ver fichas, y luego en una ficha en "Ver trabajos" o "Ver exámenes", o lo que sea, sale dos veces ese estudiante. +TODO: No limitar el número de preguntas en un examen a ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT, sino asignar PrintedQuestions dinámicamente con malloc - Version 19.250.2: Jun 17, 2020 Show valid score and valid grade in exam result. (303069 lines) + Version 19.250.3: Jun 18, 2020 Show valid score and valid grade in listing of exam results. (303113 lines) + Version 19.250.2: Jun 17, 2020 Show valid score and valid grade in one exam result. (303069 lines) Version 19.250.1: Jun 17, 2020 Fixed bug in attendance events, reported by Carlos A. Pozzo. (302977 lines) Version 19.250: Jun 17, 2020 Exam questions can be invalidated. Not finished. (302974 lines) 1 change necessary in database: diff --git a/swad_exam_print.c b/swad_exam_print.c index 167e8043c..77e09ac89 100644 --- a/swad_exam_print.c +++ b/swad_exam_print.c @@ -163,8 +163,8 @@ static void ExaPrn_ResetPrintExceptEvtCodAndUsrCod (struct ExaPrn_Print *Print) Print->TimeUTC[Dat_START_TIME] = Print->TimeUTC[Dat_END_TIME ] = (time_t) 0; Print->NumQsts = - Print->NumQstsNotBlank = - Print->NumQstsValid = 0; + Print->NumQstsValid = + Print->NumQstsNotBlank = 0; Print->Sent = false; // After creating an exam print, it's not sent Print->Score = Print->ScoreValid = 0.0; @@ -544,11 +544,10 @@ void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print) { MYSQL_RES *mysql_res; MYSQL_ROW row; - unsigned NumQsts; unsigned NumQst; /***** Get questions of an exam print from database *****/ - NumQsts = + Print->NumQsts = (unsigned) DB_QuerySELECT (&mysql_res,"can not get questions" " of an exam print", "SELECT QstCod," // row[0] @@ -562,9 +561,9 @@ void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print) Print->PrnCod); /***** Get questions *****/ - if (NumQsts == Print->NumQsts) + if (Print->NumQsts <= ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT) for (NumQst = 0; - NumQst < NumQsts; + NumQst < Print->NumQsts; NumQst++) { row = mysql_fetch_row (mysql_res); @@ -595,8 +594,8 @@ void ExaPrn_GetPrintQuestionsFromDB (struct ExaPrn_Print *Print) /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); - if (NumQsts != Print->NumQsts) - Lay_WrongExamExit (); + if (Print->NumQsts > ExaPrn_MAX_QUESTIONS_PER_EXAM_PRINT) + Lay_ShowErrorAndExit ("Too many questions."); } /*****************************************************************************/ diff --git a/swad_exam_print.h b/swad_exam_print.h index 2bb37bcbf..586211213 100644 --- a/swad_exam_print.h +++ b/swad_exam_print.h @@ -42,8 +42,8 @@ struct ExaPrn_Print long UsrCod; // User who answered the exam print time_t TimeUTC[Dat_NUM_START_END_TIME]; unsigned NumQsts; // Number of questions - unsigned NumQstsNotBlank; // Number of questions not blank unsigned NumQstsValid; // Number of valid questions (not invalidated by teachers) + unsigned NumQstsNotBlank; // Number of questions not blank bool Sent; // This exam print has been sent or not? // "Sent" means that user has clicked "Send" button after finishing double Score; // Total score of the exam print diff --git a/swad_exam_result.c b/swad_exam_result.c index 530e3ffa8..22f98eb9a 100644 --- a/swad_exam_result.c +++ b/swad_exam_result.c @@ -104,6 +104,7 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, const char *ExamsSelectedCommas); static void ExaRes_ShowResultsSummaryRow (unsigned NumResults, unsigned NumTotalQsts, + unsigned NumTotalQstsValid, unsigned NumTotalQstsNotBlank, double TotalScoreOfAllResults, double TotalGrade); @@ -710,6 +711,7 @@ static void ExaRes_ShowHeaderResults (Usr_MeOrOther_t MeOrOther) HTM_TH (1,1,"LT",Txt_START_END_TIME[Dat_END_TIME ]); HTM_TH (1,1,"LT",Txt_Session); HTM_TH (1,1,"RT",Txt_Questions); + HTM_TH (1,1,"RT","Preguntas válidas"); // TODO: Need translation!!!! HTM_TH (1,1,"RT",Txt_Non_blank_BR_questions); HTM_TH (1,1,"RT",Txt_Score); HTM_TH (1,1,"RT",Txt_Average_BR_score_BR_per_question_BR_from_0_to_1); @@ -773,21 +775,22 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, unsigned NumResult; static unsigned UniqueId = 0; char *Id; + struct ExaPrn_Print Print; struct ExaSes_Session Session; Dat_StartEndTime_t StartEndTime; - unsigned NumQstsInThisResult; - unsigned NumQstsNotBlankInThisResult; unsigned NumTotalQsts = 0; + unsigned NumTotalQstsValid = 0; unsigned NumTotalQstsNotBlank = 0; - double ScoreInThisResult; double TotalScoreOfAllResults = 0.0; + double TotalScoreValidOfAllResults = 0.0; double MaxGrade; double Grade; double TotalGrade = 0.0; unsigned Visibility; time_t TimeUTC[Dat_NUM_START_END_TIME]; - /***** Reset session *****/ + /***** Reset print and session *****/ + ExaPrn_ResetPrint (&Print); ExaSes_ResetSession (&Session); /***** Set user *****/ @@ -854,14 +857,15 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, // must be able to access exams taken in other groups NumResults = (unsigned) DB_QuerySELECT (&mysql_res,"can not get sessions results", - "SELECT exa_prints.SesCod," // row[0] - "UNIX_TIMESTAMP(exa_prints.StartTime)," // row[1] - "UNIX_TIMESTAMP(exa_prints.EndTime)," // row[2] - "exa_prints.NumQsts," // row[3] - "exa_prints.NumQstsNotBlank," // row[4] - "exa_prints.Score," // row[5] - "exa_exams.MaxGrade," // row[6] - "exa_exams.Visibility" // row[7] + "SELECT exa_prints.PrnCod," // row[0] + "exa_prints.SesCod," // row[1] + "UNIX_TIMESTAMP(exa_prints.StartTime)," // row[2] + "UNIX_TIMESTAMP(exa_prints.EndTime)," // row[3] + "exa_prints.NumQsts," // row[4] + "exa_prints.NumQstsNotBlank," // row[5] + "exa_prints.Score," // row[6] + "exa_exams.MaxGrade," // row[7] + "exa_exams.Visibility" // row[8] " FROM exa_prints,exa_sessions,exa_exams" " WHERE exa_prints.UsrCod=%ld" "%s" // Session subquery @@ -893,13 +897,17 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, { row = mysql_fetch_row (mysql_res); - /* Get session code (row[0]) */ - if ((Session.SesCod = Str_ConvertStrCodToLongCod (row[0])) < 0) + /* Get print code (row[0]) */ + if ((Print.PrnCod = Str_ConvertStrCodToLongCod (row[0])) < 0) + Lay_ShowErrorAndExit ("Wrong code of exam print."); + + /* Get session code (row[1]) */ + if ((Session.SesCod = Str_ConvertStrCodToLongCod (row[1])) < 0) Lay_ShowErrorAndExit ("Wrong code of session."); ExaSes_GetDataOfSessionByCod (&Session); - /* Get visibility (row[7]) */ - Visibility = TstVis_GetVisibilityFromStr (row[7]); + /* Get visibility (row[8]) */ + Visibility = TstVis_GetVisibilityFromStr (row[8]); /* Show session result? */ ICanViewResult = true; // Filtering is already made in the query @@ -908,12 +916,12 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, if (NumResult) HTM_TR_Begin (NULL); - /* Write start/end times (row[1], row[2] hold UTC start/end times) */ + /* Write start/end times (row[2], row[3] hold UTC start/end times) */ for (StartEndTime = (Dat_StartEndTime_t) 0; StartEndTime <= (Dat_StartEndTime_t) (Dat_NUM_START_END_TIME - 1); StartEndTime++) { - TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[1 + StartEndTime]); + TimeUTC[StartEndTime] = Dat_GetUNIXTimeFromStr (row[2 + StartEndTime]); UniqueId++; if (asprintf (&Id,"exa_res_time_%u_%u",(unsigned) StartEndTime,UniqueId) < 0) Lay_NotEnoughMemoryExit (); @@ -933,25 +941,29 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, if (ICanViewScore) { - /* Get number of questions (row[3]) */ - if (sscanf (row[3],"%u",&NumQstsInThisResult) != 1) - NumQstsInThisResult = 0; - NumTotalQsts += NumQstsInThisResult; + /* Get questions and user's answers of exam print from database */ + ExaPrn_GetPrintQuestionsFromDB (&Print); + NumTotalQsts += Print.NumQsts; - /* Get number of questions not blank (row[4]) */ - if (sscanf (row[4],"%u",&NumQstsNotBlankInThisResult) != 1) - NumQstsNotBlankInThisResult = 0; - NumTotalQstsNotBlank += NumQstsNotBlankInThisResult; + /* Compute score taking into account only valid questions */ + ExaRes_ComputeScoreValid (&Print); + NumTotalQstsValid += Print.NumQstsValid; + TotalScoreValidOfAllResults += Print.ScoreValid; + + /* Get number of questions not blank (row[5]) */ + if (sscanf (row[5],"%u",&Print.NumQstsNotBlank) != 1) + Print.NumQstsNotBlank = 0; + NumTotalQstsNotBlank += Print.NumQstsNotBlank; Str_SetDecimalPointToUS (); // To get the decimal point as a dot - /* Get score (row[5]) */ - if (sscanf (row[5],"%lf",&ScoreInThisResult) != 1) - ScoreInThisResult = 0.0; - TotalScoreOfAllResults += ScoreInThisResult; + /* Get score (row[6]) */ + if (sscanf (row[6],"%lf",&Print.Score) != 1) + Print.Score = 0.0; + TotalScoreOfAllResults += Print.Score; - /* Get maximum grade (row[6]) */ - if (sscanf (row[6],"%lf",&MaxGrade) != 1) + /* Get maximum grade (row[7]) */ + if (sscanf (row[7],"%lf",&MaxGrade) != 1) MaxGrade = 0.0; Str_SetDecimalPointToLocal (); // Return to local system @@ -960,7 +972,15 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, /* Write number of questions */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ICanViewScore) - HTM_Unsigned (NumQstsInThisResult); + HTM_Unsigned (Print.NumQsts); + else + Ico_PutIconNotVisible (); + HTM_TD_End (); + + /* Write number of valid questions */ + HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); + if (ICanViewScore) + HTM_Unsigned (Print.NumQstsValid); else Ico_PutIconNotVisible (); HTM_TD_End (); @@ -968,34 +988,34 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, /* Write number of questions not blank */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ICanViewScore) - HTM_Unsigned (NumQstsNotBlankInThisResult); + HTM_Unsigned (Print.NumQstsNotBlank); else Ico_PutIconNotVisible (); HTM_TD_End (); - /* Write score */ + /* Write score valid (taking into account only valid questions) */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ICanViewScore) - HTM_Double2Decimals (ScoreInThisResult); + HTM_Double2Decimals (Print.ScoreValid); else Ico_PutIconNotVisible (); HTM_TD_End (); - /* Write average score per question */ + /* Write average score per question (taking into account only valid questions) */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ICanViewScore) - HTM_Double2Decimals (NumQstsInThisResult ? ScoreInThisResult / - (double) NumQstsInThisResult : - 0.0); + HTM_Double2Decimals (Print.NumQstsValid ? Print.ScoreValid / + (double) Print.NumQstsValid : + 0.0); else Ico_PutIconNotVisible (); HTM_TD_End (); - /* Write grade over maximum grade */ + /* Write grade over maximum grade (taking into account only valid questions) */ HTM_TD_Begin ("class=\"DAT RT COLOR%u\"",Gbl.RowEvenOdd); if (ICanViewScore) { - Grade = TstPrn_ComputeGrade (NumQstsInThisResult,ScoreInThisResult,MaxGrade); + Grade = TstPrn_ComputeGrade (Print.NumQstsValid,Print.ScoreValid,MaxGrade); TstPrn_ShowGrade (Grade,MaxGrade); TotalGrade += Grade; } @@ -1032,10 +1052,14 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, } /***** Write totals for this user *****/ + // ExaRes_ShowResultsSummaryRow (NumResults, + // NumTotalQsts,NumTotalQstsNotBlank, + // TotalScoreOfAllResults, + // TotalGrade); ExaRes_ShowResultsSummaryRow (NumResults, - NumTotalQsts,NumTotalQstsNotBlank, - TotalScoreOfAllResults, - TotalGrade); + NumTotalQsts,NumTotalQstsValid,NumTotalQstsNotBlank, + TotalScoreValidOfAllResults, + TotalGrade); } else { @@ -1055,6 +1079,7 @@ static void ExaRes_ShowResults (struct Exa_Exams *Exams, static void ExaRes_ShowResultsSummaryRow (unsigned NumResults, unsigned NumTotalQsts, + unsigned NumTotalQstsValid, unsigned NumTotalQstsNotBlank, double TotalScoreOfAllResults, double TotalGrade) @@ -1076,6 +1101,12 @@ static void ExaRes_ShowResultsSummaryRow (unsigned NumResults, HTM_Unsigned (NumTotalQsts); HTM_TD_End (); + /***** Write total number of questions valid *****/ + HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); + if (NumResults) + HTM_Unsigned (NumTotalQstsValid); + HTM_TD_End (); + /***** Write total number of questions not blank *****/ HTM_TD_Begin ("class=\"DAT_N_LINE_TOP RM COLOR%u\"",Gbl.RowEvenOdd); if (NumResults) @@ -1475,8 +1506,11 @@ static bool ExaRes_CheckIfICanViewScore (bool ICanViewResult,unsigned Visibility static void ExaRes_ComputeScoreValid (struct ExaPrn_Print *Print) { + MYSQL_RES *mysql_res; + MYSQL_ROW row; unsigned NumQst; struct Tst_Question Question; + bool QuestionExists; /***** Initialize score valid *****/ Print->NumQstsValid = 0; @@ -1486,25 +1520,39 @@ static void ExaRes_ComputeScoreValid (struct ExaPrn_Print *Print) NumQst < Print->NumQsts; NumQst++) { - Gbl.RowEvenOdd = NumQst % 2; - - /***** Create test question *****/ - Tst_QstConstructor (&Question); + /***** Copy question code *****/ Question.QstCod = Print->PrintedQuestions[NumQst].QstCod; - /***** Get question data *****/ - ExaSet_GetQstDataFromDB (&Question); - - /***** Compute answer score *****/ - if (Question.Validity == Tst_VALID_QUESTION) + /***** Get validity and answer type from database *****/ + QuestionExists = (DB_QuerySELECT (&mysql_res,"can not get a question", + "SELECT Invalid," // row[0] + "AnsType" // row[1] + " FROM exa_set_questions" + " WHERE QstCod=%ld", + Question.QstCod) != 0); + if (QuestionExists) { - ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question); - Print->NumQstsValid++; - Print->ScoreValid += Print->PrintedQuestions[NumQst].Score; + row = mysql_fetch_row (mysql_res); + + /* Get whether the question is invalid (row[0]) */ + Question.Validity = (row[0][0] == 'Y') ? Tst_INVALID_QUESTION : + Tst_VALID_QUESTION; + + /* Get the type of answer (row[1]) */ + Question.Answer.Type = Tst_ConvertFromStrAnsTypDBToAnsTyp (row[1]); } - /***** Destroy test question *****/ - Tst_QstDestructor (&Question); + /* Free structure that stores the query result */ + DB_FreeMySQLResult (&mysql_res); + + /***** Compute answer score *****/ + if (QuestionExists) + if (Question.Validity == Tst_VALID_QUESTION) + { + ExaPrn_ComputeAnswerScore (&Print->PrintedQuestions[NumQst],&Question); + Print->NumQstsValid++; + Print->ScoreValid += Print->PrintedQuestions[NumQst].Score; + } } }