From 0bbd73d18851f6fddbaf170a991068575ea69750 Mon Sep 17 00:00:00 2001 From: acanas Date: Fri, 19 May 2023 21:46:54 +0200 Subject: [PATCH] Version 22.114: May 19, 2023 Check if a rubric is recursive. --- swad_changelog.h | 3 +- swad_project.c | 15 +--- swad_rubric.c | 52 +++++--------- swad_rubric.h | 2 +- swad_rubric_criteria.c | 158 +++++++++++++++++++++++++++++++---------- swad_rubric_criteria.h | 4 ++ 6 files changed, 149 insertions(+), 85 deletions(-) diff --git a/swad_changelog.h b/swad_changelog.h index 54ce1aa8..0b7d274e 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -629,10 +629,11 @@ 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.113 (2023-05-19)" +#define Log_PLATFORM_VERSION "SWAD 22.114 (2023-05-19)" #define CSS_FILE "swad22.107.36.css" #define JS_FILE "swad22.49.js" /* + Version 22.114: May 19, 2023 Check if a rubric is recursive. (336984 lines) Version 22.113: May 19, 2023 Check if a rubric is recursive. (336936 lines) Version 22.112: May 18, 2023 Code refactoring in options of selectors. (336794 lines) Version 22.111: May 17, 2023 A project can have more than one rubric in each category. (336691 lines) diff --git a/swad_project.c b/swad_project.c index 79cbd90c..1d9d02d8 100644 --- a/swad_project.c +++ b/swad_project.c @@ -4577,19 +4577,8 @@ static void Prj_ShowRubricsOfType (struct Prj_Projects *Projects, /* Change color for rubric criteria */ The_ChangeRowColor (); - /* Check if rubric tree is correct */ - if (Rub_CheckRubricsTree (Rubric.RubCod, - NULL)) // The stack has not yet been created - /* Write criteria of this rubric */ - RubCri_ListCriteriaInProject (Projects,Rubric.RubCod,ICanFill); - else - { - HTM_TR_Begin (NULL); - HTM_TD_Begin ("colspan=\"8\" class=\"CT %s\"",The_GetColorRows ()); - Err_RecursiveRubric (); - HTM_TD_End (); - HTM_TR_End (); - } + /* Write criteria of this rubric */ + RubCri_ListCriteriaInProject (Projects,Rubric.RubCod,ICanFill); /* Change color for next rubric */ The_ChangeRowColor (); diff --git a/swad_rubric.c b/swad_rubric.c index 86e15e1f..531dbe79 100644 --- a/swad_rubric.c +++ b/swad_rubric.c @@ -345,6 +345,7 @@ void Rub_ShowOnlyOneRubric (struct Rub_Rubrics *Rubrics) { extern const char *Hlp_ASSESSMENT_Rubrics; extern const char *Txt_Rubric; + struct Node *TOS = NULL; /***** Begin box *****/ Box_BoxBegin (NULL,Rubrics->Rubric.Title[0] ? Rubrics->Rubric.Title : @@ -357,8 +358,7 @@ void Rub_ShowOnlyOneRubric (struct Rub_Rubrics *Rubrics) true); // Show only this rubric /***** Check if rubric tree is correct *****/ - if (!Rub_CheckRubricsTree (Rubrics->Rubric.RubCod, - NULL)) // The stack has not yet been created + if (Rub_CheckIfRecursiveTree (Rubrics->Rubric.RubCod,&TOS)) Err_RecursiveRubric (); /***** Write criteria of this rubric *****/ @@ -788,6 +788,7 @@ void Rub_PutFormsOneRubric (struct Rub_Rubrics *Rubrics, [Rub_EXISTING_RUBRIC] = &Hlp_ASSESSMENT_Rubrics_edit_rubric, [Rub_NEW_RUBRIC ] = &Hlp_ASSESSMENT_Rubrics_new_rubric, }; + struct Node *TOS = NULL; /***** Begin box *****/ Box_BoxBegin (NULL, @@ -800,8 +801,7 @@ void Rub_PutFormsOneRubric (struct Rub_Rubrics *Rubrics, Rub_PutFormEditionRubric (Rubrics,ExistingNewRubric); /***** Check if rubric tree is correct *****/ - if (!Rub_CheckRubricsTree (Rubrics->Rubric.RubCod, - NULL)) // The stack has not yet been created + if (Rub_CheckIfRecursiveTree (Rubrics->Rubric.RubCod,&TOS)) Err_RecursiveRubric (); /***** Show list of criteria inside box *****/ @@ -1026,7 +1026,7 @@ static void Rub_UpdateRubric (struct Rub_Rubric *Rubric) /*****************************************************************************/ /********** Recursive function to compute the score of a criterion ***********/ /*****************************************************************************/ -// Return true if tree is ok, or false if infinite recursion +// Return true if rubric tree is recursive /* Tree Stack _______ ______ | Rub 1 | TOS (Top Of Stack)____\|___5__| @@ -1061,63 +1061,47 @@ Handwritten Handwritten |_______| Handwritten / | \ Handwritten Handwritten Handwritten */ - -bool Rub_CheckRubricsTree (long RubCod,struct Node *TOS) +bool Rub_CheckIfRecursiveTree (long RubCod,struct Node **TOS) { - struct Node *Node; - bool TreeOk; + bool RecursiveTree; MYSQL_RES *mysql_res; unsigned NumCriteria; unsigned NumCriterion; struct RubCri_Criterion Criterion; /***** Check that rubric is not yet in the stack *****/ - for (Node = TOS, TreeOk = true; - Node && TreeOk; - Node = Node->Prev) - if (Node->RubCod == RubCod) - TreeOk = false; + RecursiveTree = Rub_FindRubCodInStack (*TOS,RubCod); - if (TreeOk) + if (!RecursiveTree) { /***** Push rubric code in stack *****/ - /* Save current top of stack */ - Node = TOS; - - /* Create top of stack node */ - if ((TOS = malloc (sizeof (struct Node))) == NULL) - Err_NotEnoughMemoryExit (); - TOS->RubCod = RubCod; - TOS->Prev = Node; + Rub_PushRubCod (TOS,RubCod); /* For each criteria in this rubric... */ NumCriteria = Rub_DB_GetCriteria (&mysql_res,RubCod); for (NumCriterion = 0; - NumCriterion < NumCriteria && TreeOk; + NumCriterion < NumCriteria; NumCriterion++) { /* Get criterion data */ RubCri_GetCriterionDataFromRow (mysql_res,&Criterion); - switch (Criterion.Link.Type) - { - case Rsc_RUBRIC: - if (!Rub_CheckRubricsTree (Criterion.Link.Cod,TOS)) - TreeOk = false; + if (Criterion.Link.Type == Rsc_RUBRIC) + if (Rub_CheckIfRecursiveTree (Criterion.Link.Cod,TOS)) + { + RecursiveTree = true; break; - default: - break; - } + } } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); /***** Pop rubric code from stack *****/ - free (TOS); + Rub_PopRubCod (TOS); } - return TreeOk; + return RecursiveTree; } /*****************************************************************************/ diff --git a/swad_rubric.h b/swad_rubric.h index 1ea908ae..e14e5214 100644 --- a/swad_rubric.h +++ b/swad_rubric.h @@ -78,7 +78,7 @@ void Rub_PutFormsOneRubric (struct Rub_Rubrics *Rubrics, void Rub_ReceiveFormRubric (void); -bool Rub_CheckRubricsTree (long RubCod,struct Node *TOS); +bool Rub_CheckIfRecursiveTree (long RubCod,struct Node **TOS); //-------------------------------- Figures ------------------------------------ void Rub_GetAndShowRubricsStats (void); diff --git a/swad_rubric_criteria.c b/swad_rubric_criteria.c index d2b2864e..3776ef00 100644 --- a/swad_rubric_criteria.c +++ b/swad_rubric_criteria.c @@ -109,6 +109,7 @@ static void RubCri_ListOneOrMoreCriteriaForEdition (struct Rub_Rubrics *Rubrics, unsigned NumCriteria, MYSQL_RES *mysql_res); static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects, + struct Node **TOS, bool ICanFill, unsigned NumCriteria, MYSQL_RES *mysql_res); @@ -121,8 +122,8 @@ static void RubCri_WriteWeight (const struct RubCri_Criterion *Criterion); static void RubCri_WriteTotalLabel (unsigned ColSpan); static void RubCri_WriteTotalValue (double Total); -static double RubCri_ComputeScore (long PrjCod, - const struct RubCri_Criterion *Criterion); +static bool RubCri_ComputeRubricScore (long PrjCod,struct Node **TOS,long RubCod, + double *RubricScore); static void RubCri_PutTableHeadingForCriteria (RubCri_PutColumnForIcons_t PutColumnForIcons, RubCri_PutColumnsForScore_t PutColumnsForScore); @@ -574,15 +575,25 @@ void RubCri_ListCriteriaInProject (struct Prj_Projects *Projects,long RubCod, { MYSQL_RES *mysql_res; unsigned NumCriteria; + struct Node *TOS; /***** Get data of rubric criteria from database *****/ NumCriteria = Rub_DB_GetCriteria (&mysql_res,RubCod); /***** Show table with rubric criteria *****/ if (NumCriteria) - RubCri_ListOneOrMoreCriteriaInProject (Projects,ICanFill, + { + /* Push rubric code in stack */ + Rub_PushRubCod (&TOS,RubCod); + + /* List rubric criteria */ + RubCri_ListOneOrMoreCriteriaInProject (Projects,&TOS,ICanFill, NumCriteria,mysql_res); + /* Pop rubric code from stack */ + Rub_PopRubCod (&TOS); + } + /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); } @@ -795,6 +806,7 @@ static void RubCri_ListOneOrMoreCriteriaForEdition (struct Rub_Rubrics *Rubrics, /*****************************************************************************/ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects, + struct Node **TOS, bool ICanFill, unsigned NumCriteria, MYSQL_RES *mysql_res) @@ -803,7 +815,7 @@ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects struct RubCri_Criterion Criterion; unsigned NumCriterion; char *Anchor; - double Score; + double CriterionScore; double WeightedScore; double SumOfWeights = 0.0; double SumOfScores = 0.0; @@ -820,13 +832,6 @@ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects { /***** Get criterion data *****/ RubCri_GetCriterionDataFromRow (mysql_res,&Criterion); - SumOfWeights += Criterion.Weight; - - /***** Compute score *****/ - Score = RubCri_ComputeScore (Projects->Prj.PrjCod,&Criterion); - WeightedScore = Score * Criterion.Weight; - SumOfScores += Score; - WeightedSum += WeightedScore; /***** Build anchor string *****/ Frm_SetAnchorStr (Criterion.CriCod,&Anchor); @@ -849,6 +854,7 @@ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects switch (Criterion.Link.Type) { case Rsc_NONE: + CriterionScore = Prj_DB_GetScore (Projects->Prj.PrjCod,Criterion.CriCod); if (ICanFill) { Frm_BeginFormAnchor (ActChgPrjSco,Anchor); @@ -858,7 +864,7 @@ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects Criterion.Values[RubCri_MIN], Criterion.Values[RubCri_MAX], RubCri_SCORE_STEP, - Score, + CriterionScore, HTM_SUBMIT_ON_CHANGE,false, " class=\"INPUT_FLOAT INPUT_%s\"" " required=\"required\"", @@ -866,10 +872,15 @@ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects Frm_EndForm (); } else - HTM_Double2Decimals (Score); + HTM_Double2Decimals (CriterionScore); break; case Rsc_RUBRIC: - HTM_Double2Decimals (Score); + if (RubCri_ComputeRubricScore (Projects->Prj.PrjCod,TOS, + Criterion.Link.Cod, + &CriterionScore)) + Err_RecursiveRubric (); + else + HTM_Double2Decimals (CriterionScore); break; default: Err_NoPermission (); @@ -881,11 +892,17 @@ static void RubCri_ListOneOrMoreCriteriaInProject (struct Prj_Projects *Projects HTM_TD_Begin ("class=\"RT DAT_%s %s\"", The_GetSuffix (), The_GetColorRows ()); + WeightedScore = CriterionScore * Criterion.Weight; HTM_Double2Decimals (WeightedScore); HTM_TD_End (); /***** End row *****/ HTM_TR_End (); + + /***** Update totals *****/ + SumOfWeights += Criterion.Weight; + SumOfScores += CriterionScore; + WeightedSum += WeightedScore; } /***** Write total row *****/ @@ -989,50 +1006,119 @@ static void RubCri_WriteTotalValue (double Total) /*****************************************************************************/ /********** Recursive function to compute the score of a criterion ***********/ /*****************************************************************************/ +// Return true if rubric tree is recursive -static double RubCri_ComputeScore (long PrjCod, - const struct RubCri_Criterion *Criterion) +static bool RubCri_ComputeRubricScore (long PrjCod,struct Node **TOS,long RubCod, + double *RubricScore) { + bool RecursiveTree; MYSQL_RES *mysql_res; unsigned NumCriteria; unsigned NumCriterion; - struct RubCri_Criterion CriterionChild; - double ScoreChild; - double Score; + struct RubCri_Criterion Criterion; + double CriterionScore; - switch (Criterion->Link.Type) + /***** Initialize rubric score *****/ + *RubricScore = 0.0; + + /***** Check that rubric is not yet in the stack *****/ + RecursiveTree = Rub_FindRubCodInStack (*TOS,RubCod); + + if (!RecursiveTree) { - case Rsc_NONE: - Score = Prj_DB_GetScore (PrjCod,Criterion->CriCod); - break; - case Rsc_RUBRIC: - Score = 0.0; + /***** Push rubric code in stack *****/ + Rub_PushRubCod (TOS,RubCod); /***** Get data of rubric criteria from database *****/ - NumCriteria = Rub_DB_GetCriteria (&mysql_res,Criterion->Link.Cod); + NumCriteria = Rub_DB_GetCriteria (&mysql_res,RubCod); for (NumCriterion = 0; NumCriterion < NumCriteria; NumCriterion++) { /***** Get criterion data *****/ - RubCri_GetCriterionDataFromRow (mysql_res,&CriterionChild); + RubCri_GetCriterionDataFromRow (mysql_res,&Criterion); + + /* Get/compute criterion score */ + CriterionScore = 0.0; + switch (Criterion.Link.Type) + { + case Rsc_NONE: + CriterionScore = Prj_DB_GetScore (PrjCod,Criterion.CriCod); + break; + case Rsc_RUBRIC: + if (RubCri_ComputeRubricScore (PrjCod,TOS,Criterion.Link.Cod, + &CriterionScore)) + RecursiveTree = true; + break; + default: + break; + } /***** Compute score of this criterion in the child rubric *****/ - ScoreChild = RubCri_ComputeScore (PrjCod,&CriterionChild); - - /***** Update weighted sum *****/ - Score += CriterionChild.Weight * ScoreChild; + *RubricScore += Criterion.Weight * CriterionScore; // Update weighted sum } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); - break; - default: - Score = 0.0; - break; + + /***** Pop rubric code from stack *****/ + Rub_PopRubCod (TOS); } - return Score; + return RecursiveTree; + } + +/*****************************************************************************/ +/********************** Push/pop rubric code in stack ************************/ +/*****************************************************************************/ + +void Rub_PushRubCod (struct Node **TOS,long RubCod) + { + struct Node *Node; + + /***** Save current top of stack *****/ + Node = *TOS; + + /***** Create top of stack node *****/ + if ((*TOS = malloc (sizeof (struct Node))) == NULL) + Err_NotEnoughMemoryExit (); + (*TOS)->RubCod = RubCod; + (*TOS)->Prev = Node; // Link to previous top of stack + } + +void Rub_PopRubCod (struct Node **TOS) + { + struct Node *Node; + + if (*TOS) + { + /***** Save current top of stack *****/ + Node = (*TOS)->Prev; + + /***** Free current top of stack node *****/ + free (*TOS); + + /***** Assign new top of stack *****/ + *TOS = Node; + } + } + +/*****************************************************************************/ +/************************ Find rubric code in stack **************************/ +/*****************************************************************************/ +// Return true if found + +bool Rub_FindRubCodInStack (const struct Node *TOS,long RubCod) + { + while (TOS) + { + if (TOS->RubCod == RubCod) + return true; + + TOS = TOS->Prev; + } + + return false; } /*****************************************************************************/ diff --git a/swad_rubric_criteria.h b/swad_rubric_criteria.h index 00ed929f..0b3e9138 100644 --- a/swad_rubric_criteria.h +++ b/swad_rubric_criteria.h @@ -47,6 +47,10 @@ void RubCri_ListCriteriaForEdition (struct Rub_Rubrics *Rubrics); void RubCri_ListCriteriaInProject (struct Prj_Projects *Projects,long RubCod, bool ICanFill); +void Rub_PushRubCod (struct Node **TOS,long RubCod); +void Rub_PopRubCod (struct Node **TOS); +bool Rub_FindRubCodInStack (const struct Node *TOS,long RubCod); + double RubCri_GetParScore (void); void RubCri_GetCriterionDataFromRow (MYSQL_RES *mysql_res,