// swad_rubric_criteria.c: criteria in assessment rubrics /* SWAD (Shared Workspace At a Distance), is a web platform developed at the University of Granada (Spain), and used to support university teaching. This file is part of SWAD core. Copyright (C) 1999-2023 Antonio Caņas Vargas This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /*****************************************************************************/ /********************************* Headers ***********************************/ /*****************************************************************************/ #include // For DBL_MAX #include // To access MySQL databases #include // For string functions #include "swad_action_list.h" #include "swad_alert.h" #include "swad_box.h" #include "swad_database.h" #include "swad_error.h" #include "swad_form.h" #include "swad_global.h" #include "swad_parameter.h" #include "swad_parameter_code.h" #include "swad_rubric.h" #include "swad_rubric_criteria.h" #include "swad_rubric_database.h" /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /***************************** Private constants *****************************/ /*****************************************************************************/ // Form parameters for minimum/maximum criterion values static const char *RubCri_ParValues[RubCri_NUM_VALUES] = { [RubCri_MIN] = "MinVal", [RubCri_MAX] = "MaxVal", }; /*****************************************************************************/ /***************************** Private prototypes ****************************/ /*****************************************************************************/ static void RubCri_PutParsOneCriterion (void *Rubrics); static void RubCri_PutFormNewCriterion (struct Rub_Rubrics *Rubrics, struct RubCri_Criterion *Criterion, unsigned MaxCriInd); static void RubCri_ReceiveCriterionFieldsFromForm (struct RubCri_Criterion *Criterion); static bool RubCri_CheckCriterionTitleReceivedFromForm (const struct RubCri_Criterion *Criterion, const char NewTitle[RubCri_MAX_BYTES_TITLE + 1]); static void RubCri_ChangeValueCriterion (RubCri_ValueRange_t ValueRange); static void RubCri_CreateCriterion (struct RubCri_Criterion *Criterion); static void RubCri_ListOneOrMoreCriteriaForEdition (struct Rub_Rubrics *Rubrics, unsigned MaxCriInd, unsigned NumCriteria, MYSQL_RES *mysql_res, bool ICanEditCriteria); static void RubCri_GetCriterionDataFromRow (MYSQL_RES *mysql_res, struct RubCri_Criterion *Criterion); static void RubCri_PutTableHeadingForCriteria (void); static void RubCri_GetAndCheckPars (struct Rub_Rubrics *Rubrics, struct RubCri_Criterion *Criterion); static void RubCri_ExchangeCriteria (long RubCod, unsigned CriIndTop,unsigned CriIndBottom); /*****************************************************************************/ /*************** Put parameter to edit one rubric criterion ******************/ /*****************************************************************************/ static void RubCri_PutParsOneCriterion (void *Rubrics) { if (Rubrics) { Rub_PutPars (Rubrics); ParCod_PutPar (ParCod_Cri,((struct Rub_Rubrics *) Rubrics)->CriCod); } } /*****************************************************************************/ /******************** Get criterion data using its code **********************/ /*****************************************************************************/ void RubCri_GetCriterionDataByCod (struct RubCri_Criterion *Criterion) { MYSQL_RES *mysql_res; /***** Trivial check *****/ if (Criterion->CriCod <= 0) { /* Initialize to empty criterion */ RubCri_ResetCriterion (Criterion); return; } /***** Get data of rubric criterion from database *****/ if (Rub_DB_GetCriterionDataByCod (&mysql_res,Criterion->CriCod)) // Criterion found... RubCri_GetCriterionDataFromRow (mysql_res,Criterion); else /* Initialize to empty criterion */ RubCri_ResetCriterion (Criterion); /* Free structure that stores the query result */ DB_FreeMySQLResult (&mysql_res); } /*****************************************************************************/ /*************** Put a form to create/edit a rubric criterion ****************/ /*****************************************************************************/ static void RubCri_PutFormNewCriterion (struct Rub_Rubrics *Rubrics, struct RubCri_Criterion *Criterion, unsigned MaxCriInd) { extern const char *Txt_New_criterion; extern const char *Txt_Create_criterion; RubCri_ValueRange_t ValueRange; /***** Begin form *****/ Frm_BeginForm (ActNewRubCri); Rub_PutPars (Rubrics); /***** Begin box and table *****/ Box_BoxTableBegin (NULL,Txt_New_criterion, NULL,NULL, NULL,Box_NOT_CLOSABLE,2); /***** Table heading *****/ RubCri_PutTableHeadingForCriteria (); /***** Begin row *****/ HTM_TR_Begin (NULL); /***** Empty column for buttons *****/ HTM_TD_Begin ("class=\"BM\""); HTM_TD_End (); /***** Index *****/ HTM_TD_Begin ("class=\"RM\""); Lay_WriteIndex (MaxCriInd + 1,"BIG_INDEX"); HTM_TD_End (); /***** Title *****/ HTM_TD_Begin ("class=\"LM\""); HTM_INPUT_TEXT ("Title",RubCri_MAX_CHARS_TITLE,Criterion->Title, HTM_DONT_SUBMIT_ON_CHANGE, "id=\"Title\"" " class=\"TITLE_DESCRIPTION_WIDTH INPUT_%s\"" " required=\"required\"", The_GetSuffix ()); HTM_TD_End (); /***** Minimum and maximum values of the criterion *****/ for (ValueRange = (RubCri_ValueRange_t) 0; ValueRange <= (RubCri_ValueRange_t) (RubCri_NUM_VALUES - 1); ValueRange++) { HTM_TD_Begin ("class=\"RM\""); HTM_INPUT_FLOAT (RubCri_ParValues[ValueRange],0.0,DBL_MAX,0.1, Criterion->Values[ValueRange],false, " class=\"INPUT_%s\" required=\"required\"", The_GetSuffix ()); HTM_TD_End (); } /***** Weight of the criterion *****/ HTM_TD_Begin ("class=\"RM\""); HTM_INPUT_FLOAT ("Weight",0.0,1.0,0.01, Criterion->Weight,false, " class=\"INPUT_%s\" required=\"required\"", The_GetSuffix ()); HTM_TD_End (); /***** End row *****/ HTM_TR_End (); /***** End table, send button and end box *****/ Box_BoxTableWithButtonEnd (Btn_CREATE_BUTTON,Txt_Create_criterion); /***** End form *****/ Frm_EndForm (); } /*****************************************************************************/ /**************** Receive form to create a new rubric criterion **************/ /*****************************************************************************/ void RubCri_ReceiveFormCriterion (void) { struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get parameters *****/ Rub_GetPars (&Rubrics,true); Criterion.RubCod = Rubrics.Rubric.RubCod; /***** Get rubric data from database *****/ Rub_GetRubricDataByCod (&Rubrics.Rubric); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** If I can edit rubrics ==> receive criterion from form *****/ RubCri_ReceiveCriterionFieldsFromForm (&Criterion); if (RubCri_CheckCriterionTitleReceivedFromForm (&Criterion,Criterion.Title)) RubCri_CreateCriterion (&Criterion); // Add new criterion to database /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } static void RubCri_ReceiveCriterionFieldsFromForm (struct RubCri_Criterion *Criterion) { RubCri_ValueRange_t ValueRange; char ValueStr[64]; char WeightStr[64]; /***** Get criterion title *****/ Par_GetParText ("Title",Criterion->Title,RubCri_MAX_BYTES_TITLE); /***** Get minimum and maximum values of criterion *****/ for (ValueRange = (RubCri_ValueRange_t) 0; ValueRange <= (RubCri_ValueRange_t) (RubCri_NUM_VALUES - 1); ValueRange++) { Par_GetParText (RubCri_ParValues[ValueRange],ValueStr,sizeof (ValueStr) - 1); Criterion->Values[ValueRange] = Str_GetDoubleFromStr (ValueStr); } /***** Get criterion weight *****/ Par_GetParText ("Weight",WeightStr,sizeof (WeightStr) - 1); Criterion->Weight = Str_GetDoubleFromStr (WeightStr); } static bool RubCri_CheckCriterionTitleReceivedFromForm (const struct RubCri_Criterion *Criterion, const char NewTitle[RubCri_MAX_BYTES_TITLE + 1]) { extern const char *Txt_Already_existed_a_criterion_in_this_rubric_with_the_title_X; bool NewTitleIsCorrect; /***** Check if title is correct *****/ NewTitleIsCorrect = true; if (NewTitle[0]) // If there's an criterion title { /***** Check if old and new titles are the same (this happens when return is pressed without changes) *****/ if (strcmp (Criterion->Title,NewTitle)) // Different titles { /* If title of criterion was in database... */ if (Rub_DB_CheckIfSimilarCriterionExists (Criterion,NewTitle)) { NewTitleIsCorrect = false; Ale_ShowAlert (Ale_WARNING,Txt_Already_existed_a_criterion_in_this_rubric_with_the_title_X, Criterion->Title); } } } else // If there is not a criterion title { NewTitleIsCorrect = false; Ale_ShowAlertYouMustSpecifyTheTitle (); } return NewTitleIsCorrect; } /*****************************************************************************/ /************* Receive form to change title of rubric criterion **************/ /*****************************************************************************/ void RubCri_ChangeCriterionTitle (void) { struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; char NewTitle[RubCri_MAX_BYTES_TITLE + 1]; /***** Check if I can edit rubrics *****/ if (!Rub_CheckIfICanEditRubrics ()) Err_NoPermissionExit (); /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Receive new title from form *****/ Par_GetParText ("Title",NewTitle,RubCri_MAX_BYTES_TITLE); /***** Check if title should be changed *****/ if (RubCri_CheckCriterionTitleReceivedFromForm (&Criterion,NewTitle)) { /* Update the table changing old title by new title */ Rub_DB_UpdateCriterionTitle (Criterion.CriCod,Criterion.RubCod,NewTitle); /* Update title */ Str_Copy (Criterion.Title,NewTitle,sizeof (Criterion.Title) - 1); } /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } /*****************************************************************************/ /********* Receive form to change minimum/maximum value of criterion *********/ /*****************************************************************************/ void RubCri_ChangeMinValueCriterion (void) { RubCri_ChangeValueCriterion (RubCri_MIN); } void RubCri_ChangeMaxValueCriterion (void) { RubCri_ChangeValueCriterion (RubCri_MAX); } static void RubCri_ChangeValueCriterion (RubCri_ValueRange_t ValueRange) { struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; char ValueStr[64]; /***** Check if I can edit rubrics *****/ if (!Rub_CheckIfICanEditRubrics ()) Err_NoPermissionExit (); /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get parameters *****/ Rub_GetPars (&Rubrics,true); Criterion.RubCod = Rubrics.Rubric.RubCod; Rubrics.CriCod = Criterion.CriCod = ParCod_GetAndCheckPar (ParCod_Cri); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Receive new value from form *****/ Par_GetParText (RubCri_ParValues[ValueRange],ValueStr,sizeof (ValueStr) - 1); Criterion.Values[ValueRange] = Str_GetDoubleFromStr (ValueStr); /***** Change value *****/ /* Update the table changing old value by new value */ Rub_DB_UpdateCriterionValue (Criterion.CriCod,Criterion.RubCod, ValueRange,Criterion.Values[ValueRange]); /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } /*****************************************************************************/ /********* Receive form to change minimum/maximum value of criterion *********/ /*****************************************************************************/ void RubCri_ChangeWeightCriterion (void) { struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; char WeightStr[64]; /***** Check if I can edit rubrics *****/ if (!Rub_CheckIfICanEditRubrics ()) Err_NoPermissionExit (); /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get parameters *****/ Rub_GetPars (&Rubrics,true); Criterion.RubCod = Rubrics.Rubric.RubCod; Rubrics.CriCod = Criterion.CriCod = ParCod_GetAndCheckPar (ParCod_Cri); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Receive new value from form *****/ Par_GetParText ("Weight",WeightStr,sizeof (WeightStr) - 1); Criterion.Weight = Str_GetDoubleFromStr (WeightStr); /***** Change value *****/ /* Update the table changing old value by new value */ Rub_DB_UpdateCriterionWeight (Criterion.CriCod,Criterion.RubCod, Criterion.Weight); /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } /*****************************************************************************/ /************************ Create a new rubric criterion **********************/ /*****************************************************************************/ static void RubCri_CreateCriterion (struct RubCri_Criterion *Criterion) { extern const char *Txt_Created_new_criterion_X; unsigned MaxCriInd; /***** Get maximum criterion index *****/ MaxCriInd = Rub_DB_GetMaxCriterionIndexInRubric (Criterion->RubCod); /***** Create a new criterion *****/ Criterion->CriInd = MaxCriInd + 1; Criterion->CriCod = Rub_DB_CreateCriterion (Criterion); /***** Write success message *****/ Ale_ShowAlert (Ale_SUCCESS,Txt_Created_new_criterion_X, Criterion->Title); } /*****************************************************************************/ /********************** List the criteria of a rubric ************************/ /*****************************************************************************/ void RubCri_ListCriteria (struct Rub_Rubrics *Rubrics, struct RubCri_Criterion *Criterion) { extern const char *Hlp_ASSESSMENT_Rubrics_criteria; extern const char *Txt_Criteria; MYSQL_RES *mysql_res; unsigned MaxCriInd; unsigned NumCriteria; bool ICanEditCriteria = Rub_CheckIfEditable (&Rubrics->Rubric); /***** Get maximum criterion index *****/ MaxCriInd = Rub_DB_GetMaxCriterionIndexInRubric (Rubrics->Rubric.RubCod); /***** Get data of rubric criteria from database *****/ NumCriteria = Rub_DB_GetCriteria (&mysql_res,Rubrics->Rubric.RubCod); /***** Begin box *****/ Box_BoxBegin (NULL,Txt_Criteria, NULL,NULL, Hlp_ASSESSMENT_Rubrics_criteria,Box_NOT_CLOSABLE); /***** Show table with rubric criteria *****/ if (NumCriteria) RubCri_ListOneOrMoreCriteriaForEdition (Rubrics, MaxCriInd, NumCriteria,mysql_res, ICanEditCriteria); /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); /***** Put forms to create/edit a criterion *****/ if (ICanEditCriteria) RubCri_PutFormNewCriterion (Rubrics,Criterion,MaxCriInd); /***** End box *****/ Box_BoxEnd (); } /*****************************************************************************/ /********************** List rubric criteria for edition **********************/ /*****************************************************************************/ static void RubCri_ListOneOrMoreCriteriaForEdition (struct Rub_Rubrics *Rubrics, unsigned MaxCriInd, unsigned NumCriteria, MYSQL_RES *mysql_res, bool ICanEditCriteria) { extern const char *Txt_Criteria; extern const char *Txt_Movement_not_allowed; // Actions to change minimum/maximum criterion values static Act_Action_t RubCri_ActionsValues[RubCri_NUM_VALUES] = { [RubCri_MIN] = ActChgMinRubCri, [RubCri_MAX] = ActChgMaxRubCri, }; unsigned NumCriterion; struct RubCri_Criterion Criterion; char *Anchor; RubCri_ValueRange_t ValueRange; /***** Trivial check *****/ if (!NumCriteria) return; /***** Begin table *****/ HTM_TABLE_BeginWideMarginPadding (5); /***** Write the heading *****/ RubCri_PutTableHeadingForCriteria (); /***** Write rows *****/ for (NumCriterion = 0, The_ResetRowColor (); NumCriterion < NumCriteria; NumCriterion++, The_ChangeRowColor ()) { /***** Create criterion of questions *****/ RubCri_ResetCriterion (&Criterion); /***** Get criterion data *****/ RubCri_GetCriterionDataFromRow (mysql_res,&Criterion); /* Initialize context */ Rubrics->CriCod = Criterion.CriCod; Rubrics->CriInd = Criterion.CriInd; /***** Build anchor string *****/ Frm_SetAnchorStr (Criterion.CriCod,&Anchor); /***** Begin first row *****/ HTM_TR_Begin (NULL); /***** Icons *****/ HTM_TD_Begin ("rowspan=\"2\" class=\"BT %s\"", The_GetColorRows ()); /* Put icon to remove the criterion */ if (ICanEditCriteria) Ico_PutContextualIconToRemove (ActReqRemRubCri,NULL, RubCri_PutParsOneCriterion,Rubrics); else Ico_PutIconRemovalNotAllowed (); /* Put icon to move up the question */ if (ICanEditCriteria && Criterion.CriInd > 1) Lay_PutContextualLinkOnlyIcon (ActUp_RubCri,Anchor, RubCri_PutParsOneCriterion,Rubrics, "arrow-up.svg",Ico_BLACK); else Ico_PutIconOff ("arrow-up.svg",Ico_BLACK, Txt_Movement_not_allowed); /* Put icon to move down the criterion */ if (ICanEditCriteria && Criterion.CriInd < MaxCriInd) Lay_PutContextualLinkOnlyIcon (ActDwnRubCri,Anchor, RubCri_PutParsOneCriterion,Rubrics, "arrow-down.svg",Ico_BLACK); else Ico_PutIconOff ("arrow-down.svg",Ico_BLACK, Txt_Movement_not_allowed); HTM_TD_End (); /***** Index *****/ HTM_TD_Begin ("rowspan=\"2\" class=\"RT %s\"", The_GetColorRows ()); Lay_WriteIndex (Criterion.CriInd,"BIG_INDEX"); HTM_TD_End (); /***** Title *****/ HTM_TD_Begin ("class=\"LT %s\"",The_GetColorRows ()); HTM_ARTICLE_Begin (Anchor); if (ICanEditCriteria) { Frm_BeginFormAnchor (ActChgTitRubCri,Anchor); RubCri_PutParsOneCriterion (Rubrics); HTM_INPUT_TEXT ("Title",RubCri_MAX_CHARS_TITLE,Criterion.Title, HTM_SUBMIT_ON_CHANGE, "id=\"Title\"" " class=\"TITLE_DESCRIPTION_WIDTH INPUT_%s\"" " required=\"required\"", The_GetSuffix ()); Frm_EndForm (); } else { HTM_SPAN_Begin ("class=\"EXA_SET_TITLE\""); HTM_Txt (Criterion.Title); HTM_SPAN_End (); } HTM_ARTICLE_End (); HTM_TD_End (); /***** Minimum and maximum values of criterion *****/ for (ValueRange = (RubCri_ValueRange_t) 0; ValueRange <= (RubCri_ValueRange_t) (RubCri_NUM_VALUES - 1); ValueRange++) { HTM_TD_Begin ("class=\"RT %s\"",The_GetColorRows ()); if (ICanEditCriteria) { Frm_BeginFormAnchor (RubCri_ActionsValues[ValueRange],Anchor); RubCri_PutParsOneCriterion (Rubrics); HTM_INPUT_FLOAT (RubCri_ParValues[ValueRange],0.0,DBL_MAX,0.1, Criterion.Values[ValueRange],false, " class=\"INPUT_%s\" required=\"required\"", The_GetSuffix ()); Frm_EndForm (); } else { HTM_SPAN_Begin ("class=\"CRI_VALUE\""); HTM_Unsigned (Criterion.Values[ValueRange]); HTM_SPAN_End (); } HTM_TD_End (); } /***** Criterion weight *****/ HTM_TD_Begin ("class=\"RT %s\"",The_GetColorRows ()); if (ICanEditCriteria) { Frm_BeginFormAnchor (ActChgWeiRubCri,Anchor); RubCri_PutParsOneCriterion (Rubrics); HTM_INPUT_FLOAT ("Weight",0.0,1.0,0.01, Criterion.Weight,false, " class=\"INPUT_%s\" required=\"required\"", The_GetSuffix ()); Frm_EndForm (); } else { HTM_SPAN_Begin ("class=\"CRI_VALUE\""); HTM_Unsigned (Criterion.Weight); HTM_SPAN_End (); } HTM_TD_End (); /***** End first row *****/ HTM_TR_End (); /***** Begin second row *****/ HTM_TR_Begin (NULL); /***** Questions *****/ HTM_TD_Begin ("colspan=\"3\" class=\"LT %s\"", The_GetColorRows ()); HTM_TD_End (); /***** End second row *****/ HTM_TR_End (); /***** Free anchor string *****/ Frm_FreeAnchorStr (Anchor); } /***** End table *****/ HTM_TABLE_End (); } /*****************************************************************************/ /************************** Get rubric criteria data *************************/ /*****************************************************************************/ static void RubCri_GetCriterionDataFromRow (MYSQL_RES *mysql_res, struct RubCri_Criterion *Criterion) { MYSQL_ROW row; RubCri_ValueRange_t ValueRange; /* Get row */ row = mysql_fetch_row (mysql_res); /* row[0] CriCod row[1] RubCod row[2] CriInd row[3] Source row[4] Cod row[5] MinVal row[6] MaxVal row[7] Weight row[8] Title */ /* Get criterion code (row[0]) */ Criterion->CriCod = Str_ConvertStrCodToLongCod (row[0]); /* Get rubric code (row[0]) */ Criterion->RubCod = Str_ConvertStrCodToLongCod (row[1]); /* Get criterion index (row[2]) */ Criterion->CriInd = Str_ConvertStrToUnsigned (row[2]); /* Get source (row[3]) and code (row[4]) */ Criterion->Source = RubCri_GetSourceFromDBStr (row[3]); Criterion->Cod = Str_ConvertStrCodToLongCod (row[4]); /* Get criterion minimum and maximum values (row[5], row[6]) */ for (ValueRange = (RubCri_ValueRange_t) 0; ValueRange <= (RubCri_ValueRange_t) (RubCri_NUM_VALUES - 1); ValueRange++) Criterion->Values[ValueRange] = Str_GetDoubleFromStr (row[5 + ValueRange]); /* Get criterion weight (row[7]) */ Criterion->Weight = Str_GetDoubleFromStr (row[5 + RubCri_NUM_VALUES]); /* Get the title of the criterion (row[8]) */ Str_Copy (Criterion->Title,row[5 + RubCri_NUM_VALUES + 1],sizeof (Criterion->Title) - 1); } /*****************************************************************************/ /*********************** Get source from database string *********************/ /*****************************************************************************/ RubCri_Source_t RubCri_GetSourceFromDBStr (const char *SourceDBStr) { RubCri_Source_t Source; for (Source = (RubCri_Source_t) 0; Source <= (RubCri_Source_t) (RubCri_NUM_SOURCES - 1); Source++) if (!strcmp (RubCri_GetDBStrFromSource (Source),SourceDBStr)) return Source; return RubCri_SOURCE_DEFAULT; } /*****************************************************************************/ /*********************** Get database string from source *********************/ /*****************************************************************************/ const char *RubCri_GetDBStrFromSource (RubCri_Source_t Source) { static const char *RubCri_SourceDB[RubCri_NUM_SOURCES] = { [RubCri_FROM_TEACHER ] = "teacher", [RubCri_FROM_ANOTHER_RUBRIC] = "rubric", [RubCri_FROM_EXAM_PRINT ] = "exam", [RubCri_FROM_GAME_MATCH ] = "game", }; if (Source >= RubCri_NUM_SOURCES) Source = RubCri_SOURCE_DEFAULT; return RubCri_SourceDB[Source]; } /*****************************************************************************/ /****************** Put table heading for rubric criteria ********************/ /*****************************************************************************/ static void RubCri_PutTableHeadingForCriteria (void) { extern const char *Txt_No_INDEX; extern const char *Txt_Criterion; extern const char *Txt_Minimum; extern const char *Txt_Maximum; extern const char *Txt_Weight; /***** Begin row *****/ HTM_TR_Begin (NULL); /***** Header cells *****/ HTM_TH_Empty (1); HTM_TH (Txt_No_INDEX ,HTM_HEAD_RIGHT); HTM_TH (Txt_Criterion,HTM_HEAD_LEFT ); HTM_TH (Txt_Minimum ,HTM_HEAD_RIGHT); HTM_TH (Txt_Maximum ,HTM_HEAD_RIGHT); HTM_TH (Txt_Weight ,HTM_HEAD_RIGHT); /***** End row *****/ HTM_TR_End (); } /*****************************************************************************/ /*************************** Reset rubric criterion **************************/ /*****************************************************************************/ void RubCri_ResetCriterion (struct RubCri_Criterion *Criterion) { // Default values minimum/maximum criterion values static double RubCri_DefaultValues[RubCri_NUM_VALUES] = { [RubCri_MIN] = 0.0, [RubCri_MAX] = 0.1, }; RubCri_ValueRange_t ValueRange; Criterion->RubCod = -1L; Criterion->CriCod = -1L; Criterion->CriInd = 0; Criterion->Source = RubCri_SOURCE_DEFAULT; Criterion->Cod = -1L; for (ValueRange = (RubCri_ValueRange_t) 0; ValueRange <= (RubCri_ValueRange_t) (RubCri_NUM_VALUES - 1); ValueRange++) Criterion->Values[ValueRange] = RubCri_DefaultValues[ValueRange]; Criterion->Weight = 1.0; Criterion->Title[0] = '\0'; } /*****************************************************************************/ /***************** Request the removal of a rubric criterion *****************/ /*****************************************************************************/ void RubCri_ReqRemCriterion (void) { extern const char *Txt_Do_you_really_want_to_remove_the_criterion_X; extern const char *Txt_Remove_criterion; struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Show question and button to remove question *****/ Ale_ShowAlertAndButton (ActRemRubCri,NULL,NULL, RubCri_PutParsOneCriterion,&Rubrics, Btn_REMOVE_BUTTON,Txt_Remove_criterion, Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_criterion_X, Criterion.Title); /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } /*****************************************************************************/ /************************* Remove a rubric criterion *************************/ /*****************************************************************************/ void RubCri_RemoveCriterion (void) { extern const char *Txt_Criterion_removed; struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Remove the criterion from all tables *****/ /* Remove questions associated to criterion */ // Exa_DB_RemoveAllSetQuestionsFromSet (Criterion.CriCod,Criterion.RubCod); /* Remove the criterion itself */ Rub_DB_RemoveCriterionFromRubric (Criterion.CriCod,Criterion.RubCod); /* Change indexes of criteria greater than this */ Rub_DB_UpdateCriteriaIndexesInRubricGreaterThan (Criterion.RubCod,Criterion.CriInd); /***** Write message *****/ Ale_ShowAlert (Ale_SUCCESS,Txt_Criterion_removed); /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } /*****************************************************************************/ /*************** Move up position of a criterion in a rubric *****************/ /*****************************************************************************/ void RubCri_MoveUpCriterion (void) { extern const char *Txt_Movement_not_allowed; struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; unsigned CriIndTop; unsigned CriIndBottom; /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Get criterion index *****/ CriIndBottom = Rub_DB_GetCriIndFromCriCod (Rubrics.Rubric.RubCod,Criterion.CriCod); /***** Move up criterion *****/ if (CriIndBottom > 1) { /* Indexes of criteria to be exchanged */ CriIndTop = Rub_DB_GetPrevCriterionIndexInRubric (Rubrics.Rubric.RubCod,CriIndBottom); if (CriIndTop == 0) Err_ShowErrorAndExit ("Wrong criterion index."); /* Exchange criteria */ RubCri_ExchangeCriteria (Rubrics.Rubric.RubCod,CriIndTop,CriIndBottom); } else Ale_ShowAlert (Ale_WARNING,Txt_Movement_not_allowed); /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new rubric } /*****************************************************************************/ /************** Move down position of a criterion in a rubric ****************/ /*****************************************************************************/ void RubCri_MoveDownCriterion (void) { extern const char *Txt_Movement_not_allowed; struct Rub_Rubrics Rubrics; struct RubCri_Criterion Criterion; unsigned CriIndTop; unsigned CriIndBottom; unsigned MaxCriInd; // 0 if no criteria /***** Reset rubrics context *****/ Rub_ResetRubrics (&Rubrics); Rub_ResetRubric (&Rubrics.Rubric); RubCri_ResetCriterion (&Criterion); /***** Get and check parameters *****/ RubCri_GetAndCheckPars (&Rubrics,&Criterion); /***** Check if rubric is editable *****/ if (!Rub_CheckIfEditable (&Rubrics.Rubric)) Err_NoPermissionExit (); /***** Get criterion index *****/ CriIndTop = Rub_DB_GetCriIndFromCriCod (Rubrics.Rubric.RubCod,Criterion.CriCod); /***** Get maximum criterion index *****/ MaxCriInd = Rub_DB_GetMaxCriterionIndexInRubric (Rubrics.Rubric.RubCod); /***** Move down criterion *****/ if (CriIndTop < MaxCriInd) { /* Indexes of criteria to be exchanged */ CriIndBottom = Rub_DB_GetNextCriterionIndexInRubric (Rubrics.Rubric.RubCod,CriIndTop); if (CriIndBottom == 0) // 0 means error reading from database Err_ShowErrorAndExit ("Wrong criterion index."); /* Exchange criteria */ RubCri_ExchangeCriteria (Rubrics.Rubric.RubCod,CriIndTop,CriIndBottom); } else Ale_ShowAlert (Ale_WARNING,Txt_Movement_not_allowed); /***** Show current rubric and its criteria *****/ Rub_PutFormsOneRubric (&Rubrics,&Criterion, false); // It's not a new trubric } /*****************************************************************************/ /************************** Get and check parameters *************************/ /*****************************************************************************/ static void RubCri_GetAndCheckPars (struct Rub_Rubrics *Rubrics, struct RubCri_Criterion *Criterion) { /***** Get parameters *****/ Rub_GetPars (Rubrics,true); Criterion->CriCod = ParCod_GetAndCheckPar (ParCod_Cri); /***** Get rubric data from database *****/ Rub_GetRubricDataByCod (&Rubrics->Rubric); if (Rubrics->Rubric.CrsCod != Gbl.Hierarchy.Crs.CrsCod) Err_WrongRubricExit (); /***** Get criterion data from database *****/ RubCri_GetCriterionDataByCod (Criterion); if (Criterion->RubCod != Rubrics->Rubric.RubCod) Err_WrongCriterionExit (); Rubrics->CriCod = Criterion->CriCod; } /*****************************************************************************/ /******** Exchange the order of two consecutive criteria in a rubric *********/ /*****************************************************************************/ static void RubCri_ExchangeCriteria (long RubCod, unsigned CriIndTop,unsigned CriIndBottom) { long CriCodTop; long CriCodBottom; /***** Lock table to make the move atomic *****/ Rub_DB_LockTable (); /***** Get criterion codes of the sets to be moved *****/ CriCodTop = Rub_DB_GetCriCodFromCriInd (RubCod,CriIndTop ); CriCodBottom = Rub_DB_GetCriCodFromCriInd (RubCod,CriIndBottom); /***** Exchange indexes of sets *****/ /* Example: CriIndTop = 1; CriCodTop = 218 CriIndBottom = 2; CriCodBottom = 220 Step 1 Step 2 Step 3 +--------+--------+ +--------+--------+ +--------+--------+ +--------+--------+ | CriInd | CriCod | | CriInd | CriCod | | CriInd | CriCod | | CriInd | CriCod | +--------+--------+ +--------+--------+ +--------+--------+ +--------+--------+ | 1 | 218 |>| -2 | 218 |>| -2 | 218 |>| 2 | 218 | | 2 | 220 | | 2 | 220 | | 1 | 220 | | 1 | 220 | | 3 | 232 | | 3 | 232 | | 3 | 232 | | 3 | 232 | +--------+--------+ +--------+--------+ +--------+--------+ +--------+--------+ */ /* Step 1: change temporarily top index to minus bottom index in order to not repeat unique index (RubCod,CriInd) */ Rub_DB_UpdateCriterionIndex (-((long) CriIndBottom),CriCodTop ,RubCod); /* Step 2: change bottom index to old top index */ Rub_DB_UpdateCriterionIndex ( (long) CriIndTop ,CriCodBottom,RubCod); /* Step 3: change top index to old bottom index */ Rub_DB_UpdateCriterionIndex ( (long) CriIndBottom ,CriCodTop ,RubCod); /***** Unlock table *****/ DB_UnlockTables (); }