diff --git a/swad_rubric.c b/swad_rubric.c
new file mode 100644
index 00000000..10210b17
--- /dev/null
+++ b/swad_rubric.c
@@ -0,0 +1,1445 @@
+// swad_rubric.c: 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-2022 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 ***********************************/
+/*****************************************************************************/
+
+#define _GNU_SOURCE // For asprintf
+#include // For PATH_MAX
+#include // For NULL
+#include // For asprintf
+#include // For free
+#include // For string functions
+
+#include "swad_action_list.h"
+#include "swad_autolink.h"
+#include "swad_box.h"
+#include "swad_database.h"
+#include "swad_error.h"
+#include "swad_figure.h"
+#include "swad_form.h"
+#include "swad_global.h"
+#include "swad_hierarchy_level.h"
+#include "swad_HTML.h"
+#include "swad_pagination.h"
+#include "swad_parameter.h"
+#include "swad_role.h"
+#include "swad_rubric.h"
+#include "swad_rubric_database.h"
+
+/*****************************************************************************/
+/************** External global variables from others modules ****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/***************************** Private constants *****************************/
+/*****************************************************************************/
+
+/*****************************************************************************/
+/***************************** Private prototypes ****************************/
+/*****************************************************************************/
+
+static bool Rub_CheckIfICanEditRubrics (void);
+static void Rub_PutIconsListRubrics (void *Rubrics);
+static void Rub_PutIconToCreateNewRubric (struct Rub_Rubrics *Rubrics);
+static void Rub_PutButtonToCreateNewRubric (struct Rub_Rubrics *Rubrics);
+static void Rub_PutParamsToCreateNewRubric (void *Rubrics);
+
+static void Rub_ShowRubricMainData (struct Rub_Rubrics *Rubrics,
+ bool ShowOnlyThisRubric);
+
+static void Rub_PutIconsOneRubric (void *Rubrics);
+static void Rub_WriteAuthor (struct Rub_Rubric *Rubric);
+
+static void Rub_PutIconsToRemEditOneRubric (struct Rub_Rubrics *Rubrics);
+
+static void Rub_RemoveRubricFromAllTables (long RubCod);
+
+static void Rub_PutFormsEditionRubric (struct Rub_Rubrics *Rubrics,
+ char Txt[Cns_MAX_BYTES_TEXT + 1],
+ bool ItsANewRubric);
+static void Rub_ReceiveRubricFieldsFromForm (struct Rub_Rubric *Rubric,
+ char Txt[Cns_MAX_BYTES_TEXT + 1]);
+static bool Rub_CheckRubricFieldsReceivedFromForm (const struct Rub_Rubric *Rubric);
+
+static void Rub_CreateRubric (struct Rub_Rubric *Rubric,const char *Txt);
+static void Rub_UpdateRubric (struct Rub_Rubric *Rubric,const char *Txt);
+
+static void Rub_ListRubricCriteria (struct Rub_Rubrics *Rubrics);
+static void Rub_ListOneOrMoreCriteriaForEdition (struct Rub_Rubrics *Rubrics,
+ unsigned NumCriteria,
+ MYSQL_RES *mysql_res,
+ bool ICanEditCriteria);
+static void Rub_PutIconToAddNewCriterion (void *Rubrics);
+static void Rub_PutButtonToAddNewCriterion (struct Rub_Rubrics *Rubrics);
+
+/*****************************************************************************/
+/*************************** Reset rubrics context ***************************/
+/*****************************************************************************/
+
+void Rub_ResetRubrics (struct Rub_Rubrics *Rubrics)
+ {
+ Rubrics->LstIsRead = false; // List not read from database...
+ Rubrics->Num = 0; // Total number of rubrics
+ Rubrics->Lst = NULL; // List of rubrics
+ Rubrics->CurrentPage = 0;
+ Rubrics->Rubric.RubCod = -1L; // Current/selected rubric code
+ }
+
+/*****************************************************************************/
+/*************************** Initialize rubric to empty **********************/
+/*****************************************************************************/
+
+void Rub_ResetRubric (struct Rub_Rubric *Rubric)
+ {
+ /***** Initialize to empty rubric *****/
+ Rubric->RubCod = -1L;
+ Rubric->CrsCod = -1L;
+ Rubric->UsrCod = -1L;
+ Rubric->Title[0] = '\0';
+ Rubric->NumCriteria = 0;
+ }
+
+/*****************************************************************************/
+/***************************** Show all rubrics ******************************/
+/*****************************************************************************/
+
+void Rub_SeeAllRubrics (void)
+ {
+ struct Rub_Rubrics Rubrics;
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Get parameters *****/
+ Rub_GetParams (&Rubrics); // Return value ignored
+
+ /***** Show all rubrics *****/
+ Rub_ListAllRubrics (&Rubrics);
+ }
+
+/*****************************************************************************/
+/******************************* List all rubrics ****************************/
+/*****************************************************************************/
+
+void Rub_ListAllRubrics (struct Rub_Rubrics *Rubrics)
+ {
+ extern const char *Hlp_ASSESSMENT_Rubrics;
+ extern const char *Txt_Rubrics;
+ extern const char *Txt_Rubric;
+ extern const char *Txt_Criteria;
+ extern const char *Txt_No_rubrics;
+ struct Pagination Pagination;
+ unsigned NumRubric;
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics->Rubric);
+
+ /***** Get number of groups in current course *****/
+ if (!Gbl.Crs.Grps.NumGrps)
+ Gbl.Crs.Grps.WhichGrps = Grp_ALL_GROUPS;
+
+ /***** Get list of rubrics *****/
+ Rub_GetListRubrics (Rubrics);
+
+ /***** Compute variables related to pagination *****/
+ Pagination.NumItems = Rubrics->Num;
+ Pagination.CurrentPage = (int) Rubrics->CurrentPage;
+ Pag_CalculatePagination (&Pagination);
+ Rubrics->CurrentPage = (unsigned) Pagination.CurrentPage;
+
+ /***** Begin box *****/
+ Box_BoxBegin ("100%",Txt_Rubrics,
+ Rub_PutIconsListRubrics,Rubrics,
+ Hlp_ASSESSMENT_Rubrics,Box_NOT_CLOSABLE);
+
+ /***** Write links to pages *****/
+ Pag_WriteLinksToPagesCentered (Pag_RUBRICS,&Pagination,
+ Rubrics,-1L);
+
+ if (Rubrics->Num)
+ {
+ /***** Begin table *****/
+ HTM_TABLE_BeginWideMarginPadding (5);
+
+ /***** Table head *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TH_Span (NULL,HTM_HEAD_CENTER,1,1,"CONTEXT_COL"); // Column for contextual icons
+
+ HTM_TH_Begin (HTM_HEAD_LEFT);
+ HTM_Txt (Txt_Rubric);
+ HTM_TH_End ();
+
+ HTM_TH (Txt_Criteria,HTM_HEAD_RIGHT);
+
+ HTM_TR_End ();
+
+ /***** Write all rubrics *****/
+ for (NumRubric = Pagination.FirstItemVisible;
+ NumRubric <= Pagination.LastItemVisible;
+ NumRubric++)
+ {
+ /* Get data of this rubric */
+ Rubrics->Rubric.RubCod = Rubrics->Lst[NumRubric - 1];
+ Rub_GetDataOfRubricByCod (&Rubrics->Rubric);
+
+ /* Show a pair of rows with the main data of this rubric */
+ Rub_ShowRubricMainData (Rubrics,
+ false); // Do not show only this rubric
+ }
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+ }
+ else // No rubrics created
+ Ale_ShowAlert (Ale_INFO,Txt_No_rubrics);
+
+ /***** Write again links to pages *****/
+ Pag_WriteLinksToPagesCentered (Pag_RUBRICS,&Pagination,
+ Rubrics,-1L);
+
+ /***** Button to create a new rubric *****/
+ if (Rub_CheckIfICanEditRubrics ())
+ Rub_PutButtonToCreateNewRubric (Rubrics);
+
+ /***** End box *****/
+ Box_BoxEnd ();
+
+ /***** Free list of rubrics *****/
+ Rub_FreeListRubrics (Rubrics);
+ }
+
+/*****************************************************************************/
+/************************ Check if I can edit rubrics ************************/
+/*****************************************************************************/
+
+static bool Rub_CheckIfICanEditRubrics (void)
+ {
+ static const bool ICanEditRubrics[Rol_NUM_ROLES] =
+ {
+ [Rol_TCH ] = true,
+ [Rol_SYS_ADM] = true,
+ };
+
+ return ICanEditRubrics[Gbl.Usrs.Me.Role.Logged];
+ }
+
+/*****************************************************************************/
+/****************** Put contextual icons in list of rubrics ******************/
+/*****************************************************************************/
+
+static void Rub_PutIconsListRubrics (void *Rubrics)
+ {
+ if (Rubrics)
+ {
+ /***** Put icon to create a new rubric *****/
+ if (Rub_CheckIfICanEditRubrics ())
+ Rub_PutIconToCreateNewRubric ((struct Rub_Rubrics *) Rubrics);
+
+ /***** Put icon to show a figure *****/
+ Fig_PutIconToShowFigure (Fig_RUBRICS);
+ }
+ }
+
+/*****************************************************************************/
+/*********************** Put icon to create a new rubric ***********************/
+/*****************************************************************************/
+
+static void Rub_PutIconToCreateNewRubric (struct Rub_Rubrics *Rubrics)
+ {
+ Ico_PutContextualIconToAdd (ActFrmNewRub,NULL,
+ Rub_PutParamsToCreateNewRubric,Rubrics);
+ }
+
+/*****************************************************************************/
+/********************* Put button to create a new rubric ***********************/
+/*****************************************************************************/
+
+static void Rub_PutButtonToCreateNewRubric (struct Rub_Rubrics *Rubrics)
+ {
+ extern const char *Txt_New_rubric;
+
+ Frm_BeginForm (ActFrmNewRub);
+ Rub_PutParamsToCreateNewRubric (Rubrics);
+
+ Btn_PutConfirmButton (Txt_New_rubric);
+
+ Frm_EndForm ();
+ }
+
+/*****************************************************************************/
+/******************** Put parameters to create a new rubric ********************/
+/*****************************************************************************/
+
+static void Rub_PutParamsToCreateNewRubric (void *Rubrics)
+ {
+ if (Rubrics)
+ Pag_PutHiddenParamPagNum (Pag_RUBRICS,((struct Rub_Rubrics *) Rubrics)->CurrentPage);
+ }
+
+/*****************************************************************************/
+/******************************* Show one rubric *******************************/
+/*****************************************************************************/
+
+void Rub_SeeOneRubric (void)
+ {
+ struct Rub_Rubrics Rubrics;
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics.Rubric);
+
+ /***** Get parameters *****/
+ if ((Rubrics.Rubric.RubCod = Rub_GetParams (&Rubrics)) <= 0)
+ Err_WrongRubricExit ();
+ Rub_GetDataOfRubricByCod (&Rubrics.Rubric);
+
+ /***** Show rubric *****/
+ Rub_ShowOnlyOneRubric (&Rubrics,
+ false); // Do not list rubric criteria
+ }
+
+/*****************************************************************************/
+/******************************* Show one rubric *****************************/
+/*****************************************************************************/
+
+void Rub_ShowOnlyOneRubric (struct Rub_Rubrics *Rubrics,
+ bool ListRubricCriteria)
+ {
+ Rub_ShowOnlyOneRubricBegin (Rubrics,ListRubricCriteria);
+ Rub_ShowOnlyOneRubricEnd ();
+ }
+
+void Rub_ShowOnlyOneRubricBegin (struct Rub_Rubrics *Rubrics,
+ bool ListRubricCriteria)
+ {
+ extern const char *Hlp_ASSESSMENT_Rubrics;
+ extern const char *Txt_Rubric;
+
+ /***** Begin box *****/
+ Box_BoxBegin (NULL,Txt_Rubric,
+ Rub_PutIconsOneRubric,Rubrics,
+ Hlp_ASSESSMENT_Rubrics,Box_NOT_CLOSABLE);
+
+ /***** Show main data of this rubric *****/
+ Rub_ShowRubricMainData (Rubrics,
+ true); // Show only this rubric
+
+ /***** Write criteria of this rubric *****/
+ if (ListRubricCriteria)
+ Ale_ShowAlert (Ale_INFO,"The criteria of the rubric should be listed here.");
+ // Rub_ListRubricCriteria (Rubrics);
+ }
+
+void Rub_ShowOnlyOneRubricEnd (void)
+ {
+ /***** End box *****/
+ Box_BoxEnd ();
+ }
+
+/*****************************************************************************/
+/********** Show a pair of rows with the main data of a given rubric ***********/
+/*****************************************************************************/
+
+static void Rub_ShowRubricMainData (struct Rub_Rubrics *Rubrics,
+ bool ShowOnlyThisRubric)
+ {
+ extern const char *Txt_View_rubric;
+ extern const char *Txt_Number_of_criteria;
+ char Txt[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Begin box and table *****/
+ if (ShowOnlyThisRubric)
+ HTM_TABLE_BeginWidePadding (2);
+
+ /***** Begin first row of this rubric *****/
+ HTM_TR_Begin (NULL);
+
+ /***** Icons related to this rubric *****/
+ if (!ShowOnlyThisRubric)
+ {
+ HTM_TD_Begin ("rowspan=\"2\" class=\"CONTEXT_COL %s\"",
+ The_GetColorRows ());
+ Rub_PutIconsToRemEditOneRubric (Rubrics);
+ HTM_TD_End ();
+ }
+
+ /***** Rubric title and main data *****/
+ if (ShowOnlyThisRubric)
+ HTM_TD_Begin ("class=\"LT\"");
+ else
+ HTM_TD_Begin ("class=\"LT %s\"",The_GetColorRows ());
+
+ /* Rubric title */
+ Frm_BeginForm (ActSeeRub);
+ Rub_PutParams (Rubrics);
+ HTM_BUTTON_Submit_Begin (Txt_View_rubric,"class=\"LT BT_LINK ASG_TITLE_%s\"",
+ The_GetSuffix ());
+ HTM_Txt (Rubrics->Rubric.Title);
+ HTM_BUTTON_End ();
+ Frm_EndForm ();
+
+ /* Number of criteria */
+ HTM_DIV_Begin ("class=\"ASG_GRP_%s\"",The_GetSuffix ());
+ HTM_TxtColonNBSP (Txt_Number_of_criteria);
+ HTM_Unsigned (Rubrics->Rubric.NumCriteria);
+ HTM_DIV_End ();
+
+ /***** End 1st row of this rubric *****/
+ HTM_TR_End ();
+
+ /***** Begin 2nd row of this rubric *****/
+ HTM_TR_Begin (NULL);
+
+ /***** Author of the rubric *****/
+ if (ShowOnlyThisRubric)
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT\"");
+ else
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT %s\"",
+ The_GetColorRows ());
+ Rub_WriteAuthor (&Rubrics->Rubric);
+ HTM_TD_End ();
+
+ /***** Text of the rubric *****/
+ if (ShowOnlyThisRubric)
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT\"");
+ else
+ HTM_TD_Begin ("colspan=\"2\" class=\"LT %s\"",
+ The_GetColorRows ());
+ Rub_DB_GetRubricTxt (Rubrics->Rubric.RubCod,Txt);
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ Txt,Cns_MAX_BYTES_TEXT,false); // Convert from HTML to rigorous HTML
+ ALn_InsertLinks (Txt,Cns_MAX_BYTES_TEXT,60); // Insert links
+ HTM_DIV_Begin ("class=\"PAR DAT_%s\"",The_GetSuffix ());
+ HTM_Txt (Txt);
+ HTM_DIV_End ();
+ HTM_TD_End ();
+
+ /***** End 2nd row of this rubric *****/
+ HTM_TR_End ();
+
+ /***** End table *****/
+ if (ShowOnlyThisRubric)
+ HTM_TABLE_End ();
+ else
+ The_ChangeRowColor ();
+ }
+
+/*****************************************************************************/
+/******************** Put icons to remove/edit one rubric **********************/
+/*****************************************************************************/
+
+static void Rub_PutIconsOneRubric (void *Rubrics)
+ {
+ if (Rubrics)
+ Rub_PutIconsToRemEditOneRubric (Rubrics);
+ }
+
+/*****************************************************************************/
+/*********************** Write the author of a rubric ************************/
+/*****************************************************************************/
+
+static void Rub_WriteAuthor (struct Rub_Rubric *Rubric)
+ {
+ Usr_WriteAuthor1Line (Rubric->UsrCod,
+ false); // Not hidden
+ }
+
+/*****************************************************************************/
+/******************** Put icons to remove/edit one rubric **********************/
+/*****************************************************************************/
+
+static void Rub_PutIconsToRemEditOneRubric (struct Rub_Rubrics *Rubrics)
+ {
+ if (Rub_CheckIfICanEditRubrics ())
+ {
+ /***** Icon to remove rubric *****/
+ Ico_PutContextualIconToRemove (ActReqRemRub,NULL,
+ Rub_PutParams,Rubrics);
+ /***** Icon to edit rubric *****/
+ Ico_PutContextualIconToEdit (ActEdiOneRub,NULL,
+ Rub_PutParams,Rubrics);
+ }
+ }
+
+/*****************************************************************************/
+/**************** Put parameter to move/remove one criterion ******************/
+/*****************************************************************************/
+
+static void Rub_PutParamsOneQst (void *Rubrics)
+ {
+ if (Rubrics)
+ Rub_PutParams (Rubrics);
+ }
+
+/*****************************************************************************/
+/*********************** Params used to edit a rubric **************************/
+/*****************************************************************************/
+
+void Rub_PutParams (void *Rubrics)
+ {
+ if (Rubrics)
+ {
+ if (((struct Rub_Rubrics *) Rubrics)->Rubric.RubCod > 0)
+ Rub_PutParamRubCod (((struct Rub_Rubrics *) Rubrics)->Rubric.RubCod);
+ Pag_PutHiddenParamPagNum (Pag_RUBRICS,((struct Rub_Rubrics *) Rubrics)->CurrentPage);
+ }
+ }
+
+/*****************************************************************************/
+/******************** Write parameter with code of rubric **********************/
+/*****************************************************************************/
+
+void Rub_PutParamRubCod (long RubCod)
+ {
+ if (RubCod > 0)
+ Par_PutHiddenParamLong (NULL,"RubCod",RubCod);
+ }
+
+/*****************************************************************************/
+/********************* Get parameter with code of rubric ***********************/
+/*****************************************************************************/
+
+long Rub_GetParamRubCod (void)
+ {
+ /***** Get code of rubric *****/
+ return Par_GetParToLong ("RubCod");
+ }
+
+/*****************************************************************************/
+/****************** Get parameters used to edit a rubric *********************/
+/*****************************************************************************/
+
+long Rub_GetParams (struct Rub_Rubrics *Rubrics)
+ {
+ /***** Get other parameters *****/
+ Rubrics->CurrentPage = Pag_GetParamPagNum (Pag_RUBRICS);
+
+ /***** Get rubric code *****/
+ return Rub_GetParamRubCod ();
+ }
+
+/*****************************************************************************/
+/************************** Get list of all rubrics ****************************/
+/*****************************************************************************/
+
+void Rub_GetListRubrics (struct Rub_Rubrics *Rubrics)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ unsigned NumRubric;
+
+ /***** Free list of rubrics *****/
+ if (Rubrics->LstIsRead)
+ Rub_FreeListRubrics (Rubrics);
+
+ /***** Get list of rubrics from database *****/
+ if ((Rubrics->Num = Rub_DB_GetListRubrics (&mysql_res))) // Rubrics found...
+ {
+ /***** Create list of rubrics *****/
+ if ((Rubrics->Lst = malloc ((size_t) Rubrics->Num *
+ sizeof (*Rubrics->Lst))) == NULL)
+ Err_NotEnoughMemoryExit ();
+
+ /***** Get the rubrics codes *****/
+ for (NumRubric = 0;
+ NumRubric < Rubrics->Num;
+ NumRubric++)
+ {
+ /* Get next rubric code (row[0]) */
+ row = mysql_fetch_row (mysql_res);
+ if ((Rubrics->Lst[NumRubric] = Str_ConvertStrCodToLongCod (row[0])) <= 0)
+ Err_WrongRubricExit ();
+ }
+ }
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ Rubrics->LstIsRead = true;
+ }
+
+/*****************************************************************************/
+/********************** Get rubric data using its code *************************/
+/*****************************************************************************/
+
+void Rub_GetDataOfRubricByCod (struct Rub_Rubric *Rubric)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+
+ /***** Get data of rubric from database *****/
+ if (Rub_DB_GetDataOfRubricByCod (&mysql_res,Rubric->RubCod)) // Rubric found...
+ {
+ /* Get row */
+ row = mysql_fetch_row (mysql_res);
+
+ /* Get code of the rubric (row[0]) */
+ Rubric->RubCod = Str_ConvertStrCodToLongCod (row[0]);
+
+ /* Get code of the course (row[1]) */
+ Rubric->CrsCod = Str_ConvertStrCodToLongCod (row[1]);
+
+ /* Get author of the rubric (row[2]) */
+ Rubric->UsrCod = Str_ConvertStrCodToLongCod (row[2]);
+
+ /* Get the title of the rubric (row[3]) */
+ Str_Copy (Rubric->Title,row[3],sizeof (Rubric->Title) - 1);
+
+ /* Get number of criteria */
+ Rubric->NumCriteria = Rub_DB_GetNumCriteriaInRubric (Rubric->RubCod);
+ }
+ else
+ /* Initialize to empty rubric */
+ Rub_ResetRubric (Rubric);
+
+ /* Free structure that stores the query result */
+ DB_FreeMySQLResult (&mysql_res);
+ }
+
+/*****************************************************************************/
+/***************************** Free list of rubrics ****************************/
+/*****************************************************************************/
+
+void Rub_FreeListRubrics (struct Rub_Rubrics *Rubrics)
+ {
+ if (Rubrics->LstIsRead && Rubrics->Lst)
+ {
+ /***** Free memory used by the list of rubrics *****/
+ free (Rubrics->Lst);
+ Rubrics->Lst = NULL;
+ Rubrics->Num = 0;
+ Rubrics->LstIsRead = false;
+ }
+ }
+
+/*****************************************************************************/
+/*************** Ask for confirmation of removing of a rubric ******************/
+/*****************************************************************************/
+
+void Rub_AskRemRubric (void)
+ {
+ extern const char *Txt_Do_you_really_want_to_remove_the_game_X;
+ extern const char *Txt_Remove_game;
+ struct Rub_Rubrics Rubrics;
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics.Rubric);
+
+ /***** Get parameters *****/
+ if ((Rubrics.Rubric.RubCod = Rub_GetParams (&Rubrics)) <= 0)
+ Err_WrongRubricExit ();
+
+ /***** Get data of the rubric from database *****/
+ Rub_GetDataOfRubricByCod (&Rubrics.Rubric);
+ if (!Rub_CheckIfICanEditRubrics ())
+ Err_NoPermissionExit ();
+
+ /***** Show criterion and button to remove rubric *****/
+ Ale_ShowAlertAndButton (ActRemGam,NULL,NULL,
+ Rub_PutParams,&Rubrics,
+ Btn_REMOVE_BUTTON,Txt_Remove_game,
+ Ale_QUESTION,Txt_Do_you_really_want_to_remove_the_game_X,
+ Rubrics.Rubric.Title);
+
+ /***** Show rubrics again *****/
+ Rub_ListAllRubrics (&Rubrics);
+ }
+
+/*****************************************************************************/
+/******************************* Remove a rubric *******************************/
+/*****************************************************************************/
+
+void Rub_RemoveRubric (void)
+ {
+ extern const char *Txt_Rubric_X_removed;
+ struct Rub_Rubrics Rubrics;
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics.Rubric);
+
+ /***** Get rubric code *****/
+ if ((Rubrics.Rubric.RubCod = Rub_GetParamRubCod ()) <= 0)
+ Err_WrongRubricExit ();
+
+ /***** Get data of the rubric from database *****/
+ Rub_GetDataOfRubricByCod (&Rubrics.Rubric);
+ if (!Rub_CheckIfICanEditRubrics ())
+ Err_NoPermissionExit ();
+
+ /***** Remove rubric from all tables *****/
+ Rub_RemoveRubricFromAllTables (Rubrics.Rubric.RubCod);
+
+ /***** Write message to show the change made *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_Rubric_X_removed,
+ Rubrics.Rubric.Title);
+
+ /***** Show rubrics again *****/
+ Rub_ListAllRubrics (&Rubrics);
+ }
+
+/*****************************************************************************/
+/*********************** Remove rubric from all tables *************************/
+/*****************************************************************************/
+
+static void Rub_RemoveRubricFromAllTables (long RubCod)
+ {
+ /***** Remove rubric criteria *****/
+ Rub_DB_RemoveRubricCriteria (RubCod);
+
+ /***** Remove rubric *****/
+ Rub_DB_RemoveRubric (RubCod);
+ }
+
+/*****************************************************************************/
+/*********************** Remove all rubrics of a course ************************/
+/*****************************************************************************/
+
+void Rub_RemoveCrsRubrics (long CrsCod)
+ {
+ /***** Remove the criteria in rubrics *****/
+ Rub_DB_RemoveCrsRubricCriteria (CrsCod);
+
+ /***** Remove the rubrics *****/
+ Rub_DB_RemoveCrsRubrics (CrsCod);
+ }
+
+/*****************************************************************************/
+/************************ List the criteria in a rubric ********************/
+/*****************************************************************************/
+
+void Rub_ListRubric (void)
+ {
+ struct Rub_Rubrics Rubrics;
+ char Txt[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics.Rubric);
+
+ /***** Get parameters *****/
+ if ((Rubrics.Rubric.RubCod = Rub_GetParams (&Rubrics)) <= 0)
+ Err_WrongRubricExit ();
+
+ /***** Get rubric data *****/
+ Rub_GetDataOfRubricByCod (&Rubrics.Rubric);
+ Rub_DB_GetRubricTxt (Rubrics.Rubric.RubCod,Txt);
+
+ /***** Show rubric *****/
+ Rub_ShowOnlyOneRubric (&Rubrics,
+ true); // List rubric criteria
+ }
+
+/*****************************************************************************/
+/**************** Request the creation or edition of a rubric ******************/
+/*****************************************************************************/
+
+void Rub_RequestCreatOrEditRubric (void)
+ {
+ struct Rub_Rubrics Rubrics;
+ bool ItsANewRubric;
+ char Txt[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics.Rubric);
+
+ /***** Check if I can edit rubrics *****/
+ if (!Rub_CheckIfICanEditRubrics ())
+ Err_NoPermissionExit ();
+
+ /***** Get parameters *****/
+ ItsANewRubric = ((Rubrics.Rubric.RubCod = Rub_GetParams (&Rubrics)) <= 0);
+
+ /***** Get rubric data *****/
+ if (ItsANewRubric)
+ {
+ /* Initialize to empty rubric */
+ Rub_ResetRubric (&Rubrics.Rubric);
+ Txt[0] = '\0';
+ }
+ else
+ {
+ /* Get rubric data from database */
+ Rub_GetDataOfRubricByCod (&Rubrics.Rubric);
+ Rub_DB_GetRubricTxt (Rubrics.Rubric.RubCod,Txt);
+ }
+
+ /***** Put forms to create/edit a rubric *****/
+ Rub_PutFormsEditionRubric (&Rubrics,Txt,ItsANewRubric);
+
+ /***** Show rubrics or criteria *****/
+ if (ItsANewRubric)
+ /* Show rubrics again */
+ Rub_ListAllRubrics (&Rubrics);
+ else
+ /* Show criteria of the rubric ready to be edited */
+ Rub_ListRubricCriteria (&Rubrics);
+ }
+
+/*****************************************************************************/
+/******************** Put a form to create/edit a rubric *********************/
+/*****************************************************************************/
+
+static void Rub_PutFormsEditionRubric (struct Rub_Rubrics *Rubrics,
+ char Txt[Cns_MAX_BYTES_TEXT + 1],
+ bool ItsANewRubric)
+ {
+ extern const char *Hlp_ASSESSMENT_Rubrics_new_rubric;
+ extern const char *Hlp_ASSESSMENT_Rubrics_edit_rubric;
+ extern const char *Txt_New_rubric;
+ extern const char *Txt_Edit_rubric;
+ extern const char *Txt_Title;
+ extern const char *Txt_Description;
+ extern const char *Txt_Create_rubric;
+ extern const char *Txt_Save_changes;
+
+ /***** Begin form *****/
+ Frm_BeginForm (ItsANewRubric ? ActNewRub :
+ ActChgRub);
+ Rub_PutParams (Rubrics);
+
+ /***** Begin box and table *****/
+ if (ItsANewRubric)
+ Box_BoxTableBegin (NULL,Txt_New_rubric,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Rubrics_new_rubric,Box_NOT_CLOSABLE,2);
+ else
+ Box_BoxTableBegin (NULL,
+ Rubrics->Rubric.Title[0] ? Rubrics->Rubric.Title :
+ Txt_Edit_rubric,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Rubrics_edit_rubric,Box_NOT_CLOSABLE,2);
+
+ /***** Rubric title *****/
+ HTM_TR_Begin (NULL);
+
+ /* Label */
+ Frm_LabelColumn ("RT","Title",Txt_Title);
+
+ /* Data */
+ HTM_TD_Begin ("class=\"LT\"");
+ HTM_INPUT_TEXT ("Title",Rub_MAX_CHARS_TITLE,Rubrics->Rubric.Title,
+ HTM_DONT_SUBMIT_ON_CHANGE,
+ "id=\"Title\""
+ " class=\"TITLE_DESCRIPTION_WIDTH INPUT_%s\""
+ " required=\"required\"",
+ The_GetSuffix ());
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** Rubric text *****/
+ HTM_TR_Begin (NULL);
+
+ /* Label */
+ Frm_LabelColumn ("RT","Txt",Txt_Description);
+
+ /* Data */
+ HTM_TD_Begin ("class=\"LT\"");
+ HTM_TEXTAREA_Begin ("id=\"Txt\" name=\"Txt\" rows=\"5\""
+ " class=\"TITLE_DESCRIPTION_WIDTH INPUT_%s\"",
+ The_GetSuffix ());
+ HTM_Txt (Txt);
+ HTM_TEXTAREA_End ();
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** End table, send button and end box *****/
+ if (ItsANewRubric)
+ Box_BoxTableWithButtonEnd (Btn_CREATE_BUTTON,Txt_Create_rubric);
+ else
+ Box_BoxTableWithButtonEnd (Btn_CONFIRM_BUTTON,Txt_Save_changes);
+
+ /***** End form *****/
+ Frm_EndForm ();
+ }
+
+/*****************************************************************************/
+/********************** Receive form to create a new rubric ********************/
+/*****************************************************************************/
+
+void Rub_ReceiveFormRubric (void)
+ {
+ struct Rub_Rubrics Rubrics;
+ bool ItsANewRubric;
+ char Txt[Cns_MAX_BYTES_TEXT + 1];
+
+ /***** Reset rubrics context *****/
+ Rub_ResetRubrics (&Rubrics);
+
+ /***** Reset rubric *****/
+ Rub_ResetRubric (&Rubrics.Rubric);
+
+ /***** Check if I can edit rubrics *****/
+ if (!Rub_CheckIfICanEditRubrics ())
+ Err_NoPermissionExit ();
+
+ /***** Get parameters *****/
+ ItsANewRubric = ((Rubrics.Rubric.RubCod = Rub_GetParams (&Rubrics)) <= 0);
+
+ /***** If I can edit rubrics ==> receive rubric from form *****/
+ if (Rub_CheckIfICanEditRubrics ())
+ {
+ Rub_ReceiveRubricFieldsFromForm (&Rubrics.Rubric,Txt);
+ if (Rub_CheckRubricFieldsReceivedFromForm (&Rubrics.Rubric))
+ {
+ /***** Create a new rubric or update an existing one *****/
+ if (ItsANewRubric)
+ Rub_CreateRubric (&Rubrics.Rubric,Txt); // Add new rubric to database
+ else
+ Rub_UpdateRubric (&Rubrics.Rubric,Txt); // Update rubric data in database
+
+ /***** Put forms to edit the rubric created or updated *****/
+ Rub_PutFormsEditionRubric (&Rubrics,Txt,
+ false); // No new rubric
+
+ /***** Show criteria of the rubric ready to be edited ******/
+ Rub_ListRubricCriteria (&Rubrics);
+ }
+ else
+ {
+ /***** Put forms to create/edit the rubric *****/
+ Rub_PutFormsEditionRubric (&Rubrics,Txt,ItsANewRubric);
+
+ /***** Show rubrics or criteria *****/
+ if (ItsANewRubric)
+ /* Show rubrics again */
+ Rub_ListAllRubrics (&Rubrics);
+ else
+ /* Show criteria of the rubric ready to be edited */
+ Rub_ListRubricCriteria (&Rubrics);
+ }
+ }
+ else
+ Err_NoPermissionExit ();
+ }
+
+static void Rub_ReceiveRubricFieldsFromForm (struct Rub_Rubric *Rubric,
+ char Txt[Cns_MAX_BYTES_TEXT + 1])
+ {
+ /***** Get rubric title *****/
+ Par_GetParToText ("Title",Rubric->Title,Rub_MAX_BYTES_TITLE);
+
+ /***** Get rubric text *****/
+ Par_GetParToHTML ("Txt",Txt,Cns_MAX_BYTES_TEXT); // Store in HTML format (not rigorous)
+ }
+
+static bool Rub_CheckRubricFieldsReceivedFromForm (const struct Rub_Rubric *Rubric)
+ {
+ extern const char *Txt_Already_existed_a_game_with_the_title_X;
+ bool NewRubricIsCorrect;
+
+ /***** Check if title is correct *****/
+ NewRubricIsCorrect = true;
+ if (Rubric->Title[0]) // If there's a rubric title
+ {
+ /* If title of rubric was in database... */
+ if (Rub_DB_CheckIfSimilarRubricExists (Rubric))
+ {
+ NewRubricIsCorrect = false;
+ Ale_ShowAlert (Ale_WARNING,Txt_Already_existed_a_game_with_the_title_X,
+ Rubric->Title);
+ }
+ }
+ else // If there is not a rubric title
+ {
+ NewRubricIsCorrect = false;
+ Ale_ShowAlertYouMustSpecifyTheTitle ();
+ }
+
+ return NewRubricIsCorrect;
+ }
+
+/*****************************************************************************/
+/**************************** Create a new rubric ******************************/
+/*****************************************************************************/
+
+static void Rub_CreateRubric (struct Rub_Rubric *Rubric,const char *Txt)
+ {
+ extern const char *Txt_Created_new_game_X;
+
+ /***** Create a new rubric *****/
+ Rubric->RubCod = Rub_DB_CreateRubric (Rubric,Txt);
+
+ /***** Write success message *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_Created_new_game_X,
+ Rubric->Title);
+ }
+
+/*****************************************************************************/
+/************************* Update an existing rubric *************************/
+/*****************************************************************************/
+
+static void Rub_UpdateRubric (struct Rub_Rubric *Rubric,const char *Txt)
+ {
+ extern const char *Txt_The_rubric_has_been_modified;
+
+ /***** Update the data of the rubric *****/
+ Rub_DB_UpdateRubric (Rubric,Txt);
+
+ /***** Write success message *****/
+ Ale_ShowAlert (Ale_SUCCESS,Txt_The_rubric_has_been_modified);
+ }
+
+/*****************************************************************************/
+/****************** Write parameter with index of criterion ******************/
+/*****************************************************************************/
+
+void Rub_PutParamCriInd (unsigned CriInd)
+ {
+ Par_PutHiddenParamUnsigned (NULL,"CriInd",CriInd);
+ }
+
+/*****************************************************************************/
+/******************* Get parameter with index of criterion *******************/
+/*****************************************************************************/
+
+unsigned Rub_GetParamCriInd (void)
+ {
+ long CriInd;
+
+ if ((CriInd = Par_GetParToLong ("CriInd")) <= 0) // In rubrics, criterion index should be 1, 2, 3...
+ Err_WrongCriterionIndexExit ();
+
+ return (unsigned) CriInd;
+ }
+
+/*****************************************************************************/
+/********************** List the criteria of a rubric **********************/
+/*****************************************************************************/
+
+static void Rub_ListRubricCriteria (struct Rub_Rubrics *Rubrics)
+ {
+ extern const char *Hlp_ASSESSMENT_Rubrics_criteria;
+ extern const char *Txt_Criteria;
+ MYSQL_RES *mysql_res;
+ unsigned NumCriteria;
+ bool ICanEditCriteria = Rub_CheckIfICanEditRubrics ();
+
+ /***** Get data of criteria from database *****/
+ NumCriteria = Rub_DB_GetRubricCriteriaBasic (&mysql_res,Rubrics->Rubric.RubCod);
+
+ /***** Begin box *****/
+ if (ICanEditCriteria)
+ Box_BoxBegin (NULL,Txt_Criteria,
+ Rub_PutIconToAddNewCriterion,Rubrics,
+ Hlp_ASSESSMENT_Rubrics_criteria,Box_NOT_CLOSABLE);
+ else
+ Box_BoxBegin (NULL,Txt_Criteria,
+ NULL,NULL,
+ Hlp_ASSESSMENT_Rubrics_criteria,Box_NOT_CLOSABLE);
+
+ /***** Show table with criteria *****/
+ if (NumCriteria)
+ Rub_ListOneOrMoreCriteriaForEdition (Rubrics,NumCriteria,mysql_res,
+ ICanEditCriteria);
+
+ /***** Put button to add a new criterion in this rubric *****/
+ if (ICanEditCriteria) // I can edit criteria
+ Rub_PutButtonToAddNewCriterion (Rubrics);
+
+ /***** Free structure that stores the query result *****/
+ DB_FreeMySQLResult (&mysql_res);
+
+ /***** End box *****/
+ Box_BoxEnd ();
+ }
+
+/*****************************************************************************/
+/********************* List rubric criteria for edition ***********************/
+/*****************************************************************************/
+
+static void Rub_ListOneOrMoreCriteriaForEdition (struct Rub_Rubrics *Rubrics,
+ unsigned NumCriteria,
+ MYSQL_RES *mysql_res,
+ bool ICanEditCriteria)
+ {
+ extern const char *Txt_Criteria;
+ extern const char *Txt_No_INDEX;
+ extern const char *Txt_Code;
+ extern const char *Txt_Tags;
+ extern const char *Txt_Criterion;
+ extern const char *Txt_Movement_not_allowed;
+ unsigned NumCri;
+ unsigned CriInd;
+ unsigned MaxCriInd;
+ MYSQL_ROW row;
+ struct Rub_Criterion Criterion;
+ char StrCriInd[Cns_MAX_DECIMAL_DIGITS_UINT + 1];
+ bool CriterionExists;
+ char *Anchor = NULL;
+
+ /***** Trivial check *****/
+ if (!NumCriteria)
+ return;
+
+ /***** Get maximum criterion index *****/
+ MaxCriInd = Rub_DB_GetMaxCriterionIndexInRubric (Rubrics->Rubric.RubCod); // 0 is no criteria in rubric
+
+ /***** Write the heading *****/
+ HTM_TABLE_BeginWideMarginPadding (5);
+
+ HTM_TR_Begin (NULL);
+ HTM_TH_Empty (1);
+ HTM_TH (Txt_No_INDEX ,HTM_HEAD_CENTER);
+ HTM_TH (Txt_Code ,HTM_HEAD_CENTER);
+ HTM_TH (Txt_Tags ,HTM_HEAD_CENTER);
+ HTM_TH (Txt_Criterion,HTM_HEAD_CENTER);
+ HTM_TR_End ();
+
+ /***** Write rows *****/
+ for (NumCri = 0, The_ResetRowColor ();
+ NumCri < NumCriteria;
+ NumCri++, The_ChangeRowColor ())
+ {
+ /***** Create rubric criterion *****/
+ Rub_CriterionConstructor (&Criterion);
+
+ /***** Get criterion data *****/
+ row = mysql_fetch_row (mysql_res);
+ /*
+ row[0] QstCod
+ row[1] QstInd
+ */
+ /* Get criterion code (row[0]) */
+ Criterion.CriCod = Str_ConvertStrCodToLongCod (row[0]);
+
+ /* Get criterion index (row[1]) */
+ CriInd = Str_ConvertStrToUnsigned (row[1]);
+ snprintf (StrCriInd,sizeof (StrCriInd),"%u",CriInd);
+
+ /***** Build anchor string *****/
+ Frm_SetAnchorStr (Criterion.CriCod,&Anchor);
+
+ /***** Begin row *****/
+ HTM_TR_Begin (NULL);
+
+ /***** Icons *****/
+ HTM_TD_Begin ("class=\"BT %s\"",The_GetColorRows ());
+
+ /* Put icon to remove the criterion */
+ if (ICanEditCriteria)
+ Ico_PutContextualIconToRemove (ActReqRemGamQst,NULL,
+ Rub_PutParamsOneQst,Rubrics);
+ else
+ Ico_PutIconRemovalNotAllowed ();
+
+ /* Put icon to move up the criterion */
+ if (ICanEditCriteria && CriInd > 1)
+ Lay_PutContextualLinkOnlyIcon (ActUp_GamQst,Anchor,
+ Rub_PutParamsOneQst,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 && CriInd < MaxCriInd)
+ Lay_PutContextualLinkOnlyIcon (ActDwnGamQst,Anchor,
+ Rub_PutParamsOneQst,Rubrics,
+ "arrow-down.svg",Ico_BLACK);
+ else
+ Ico_PutIconOff ("arrow-down.svg",Ico_BLACK,
+ Txt_Movement_not_allowed);
+
+ /* Put icon to edit the criterion */
+ if (ICanEditCriteria)
+ Ico_PutContextualIconToEdit (ActEdiOneTstQst,NULL,
+ Rub_PutParamCriCod,&Criterion.CriCod);
+
+ HTM_TD_End ();
+
+ /***** Criterion *****/
+ CriterionExists = Rub_GetCriterionDataFromDB (&Criterion);
+ Rub_ListCriterionForEdition (&Criterion,CriInd,CriterionExists,Anchor);
+
+ /***** End row *****/
+ HTM_TR_End ();
+
+ /***** Free anchor string *****/
+ Frm_FreeAnchorStr (Anchor);
+
+ /***** Destroy rubric criterion *****/
+ Rub_CriterionDestructor (&Criterion);
+ }
+
+ /***** End table *****/
+ HTM_TABLE_End ();
+ }
+
+/*****************************************************************************/
+/***************** Put icon to add a new criterion to rubric *****************/
+/*****************************************************************************/
+
+static void Rub_PutIconToAddNewCriterion (void *Rubrics)
+ {
+ Ico_PutContextualIconToAdd (ActAddOneGamQst,NULL,Rub_PutParams,Rubrics);
+ }
+
+/*****************************************************************************/
+/***************** Put button to add new criteria to rubric ******************/
+/*****************************************************************************/
+
+static void Rub_PutButtonToAddNewCriterion (struct Rub_Rubrics *Rubrics)
+ {
+ extern const char *Txt_Add_criteria;
+
+ Frm_BeginForm (ActAddOneGamQst);
+ Rub_PutParams (Rubrics);
+
+ Btn_PutConfirmButton (Txt_Add_criteria);
+
+ Frm_EndForm ();
+ }
+
+/*****************************************************************************/
+/************************** Show stats about rubrics *************************/
+/*****************************************************************************/
+
+void Rub_GetAndShowRubricsStats (void)
+ {
+ extern const char *Hlp_ANALYTICS_Figures_rubrics;
+ extern const char *Txt_FIGURE_TYPES[Fig_NUM_FIGURES];
+ extern const char *Txt_Number_of_BR_rubrics;
+ extern const char *Txt_Number_of_BR_courses_with_BR_rubrics;
+ extern const char *Txt_Average_number_BR_of_rubrics_BR_per_course;
+ extern const char *Txt_Average_number_BR_of_criteria_BR_per_rubric;
+ unsigned NumRubrics;
+ unsigned NumCoursesWithRubrics = 0;
+ double NumRubricsPerCourse = 0.0;
+ double NumCriteriaPerRubric = 0.0;
+
+ // Err_ShowErrorAndExit ("Rub_GetAndShowRubricsStats ()");
+
+ /***** Get the number of rubrics from this location *****/
+ if ((NumRubrics = Rub_DB_GetNumRubrics (Gbl.Scope.Current)))
+ {
+ if ((NumCoursesWithRubrics = Rub_DB_GetNumCoursesWithRubrics (Gbl.Scope.Current)) != 0)
+ NumRubricsPerCourse = (double) NumRubrics /
+ (double) NumCoursesWithRubrics;
+ NumCriteriaPerRubric = Rub_DB_GetNumCriteriaPerRubric (Gbl.Scope.Current);
+ }
+
+ /***** Begin box and table *****/
+ Box_BoxTableBegin (NULL,Txt_FIGURE_TYPES[Fig_RUBRICS],
+ NULL,NULL,
+ Hlp_ANALYTICS_Figures_rubrics,Box_NOT_CLOSABLE,2);
+
+ /***** Write table heading *****/
+ HTM_TR_Begin (NULL);
+ HTM_TH (Txt_Number_of_BR_rubrics ,HTM_HEAD_RIGHT);
+ HTM_TH (Txt_Number_of_BR_courses_with_BR_rubrics ,HTM_HEAD_RIGHT);
+ HTM_TH (Txt_Average_number_BR_of_rubrics_BR_per_course ,HTM_HEAD_RIGHT);
+ HTM_TH (Txt_Average_number_BR_of_criteria_BR_per_rubric,HTM_HEAD_RIGHT);
+ HTM_TR_End ();
+
+ /***** Write number of rubrics *****/
+ HTM_TR_Begin (NULL);
+
+ HTM_TD_Begin ("class=\"RM DAT_%s\"",The_GetSuffix ());
+ HTM_Unsigned (NumRubrics);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"RM DAT_%s\"",The_GetSuffix ());
+ HTM_Unsigned (NumCoursesWithRubrics);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"RM DAT_%s\"",The_GetSuffix ());
+ HTM_Double2Decimals (NumRubricsPerCourse);
+ HTM_TD_End ();
+
+ HTM_TD_Begin ("class=\"RM DAT_%s\"",The_GetSuffix ());
+ HTM_Double2Decimals (NumCriteriaPerRubric);
+ HTM_TD_End ();
+
+ HTM_TR_End ();
+
+ /***** End table and box *****/
+ Box_BoxTableEnd ();
+ }
+
+/*****************************************************************************/
+/********************** Initialize a new citerion to zero ********************/
+/*****************************************************************************/
+
+void Rub_CriterionConstructor (struct Rub_Criterion *Criterion)
+ {
+ Criterion->CriCod = -1L;
+ }
+
+/*****************************************************************************/
+/*************** Free memory allocated for rubric criterion ******************/
+/*****************************************************************************/
+
+void Rub_CriterionDestructor (struct Rub_Criterion *Criterion)
+ {
+ Criterion->CriCod = -1L;
+ }
+
+/*****************************************************************************/
+/*********** Put parameter with criterion code to edit, remove... ************/
+/*****************************************************************************/
+
+void Rub_PutParamCriCod (void *CriCod) // Should be a pointer to long
+ {
+ if (CriCod)
+ if (*((long *) CriCod) > 0) // If criterion exists
+ Par_PutHiddenParamLong (NULL,"CriCod",*((long *) CriCod));
+ }
+
+/*****************************************************************************/
+/*************** Get data of a rubric criterion from database ****************/
+/*****************************************************************************/
+
+bool Rub_GetCriterionDataFromDB (struct Rub_Criterion *Criterion)
+ {
+ MYSQL_RES *mysql_res;
+ MYSQL_ROW row;
+ bool CriterionExists;
+
+ /***** Get question data from database *****/
+ if ((CriterionExists = (Rub_DB_GetCriterionData (&mysql_res,Criterion->CriCod) != 0)))
+ {
+ row = mysql_fetch_row (mysql_res);
+
+ /* Get the title (row[0]) */
+ Criterion->Title[0] = '\0';
+ if (row[0])
+ if (row[0][0])
+ Str_Copy (Criterion->Title,row[0],Cns_MAX_BYTES_TEXT);
+ }
+
+ /* Free structure that stores the query result */
+ DB_FreeMySQLResult (&mysql_res);
+
+ return CriterionExists;
+ }
+
+/*****************************************************************************/
+/******************* List a rubric criterion for edition *********************/
+/*****************************************************************************/
+
+void Rub_ListCriterionForEdition (struct Rub_Criterion *Criterion,
+ unsigned CriInd,bool CriterionExists,
+ const char *Anchor)
+ {
+ extern const char *Txt_Criterion_removed;
+
+ /***** Number of criterion *****/
+ HTM_TD_Begin ("class=\"RT %s\"",The_GetColorRows ());
+ Rub_WriteNumCriterion (CriInd,"BIG_INDEX");
+ HTM_TD_End ();
+
+ /***** Write criterion code *****/
+ HTM_TD_Begin ("class=\"CT DAT_SMALL_%s %s CT\"",
+ The_GetSuffix (),The_GetColorRows ());
+ HTM_TxtF ("%ld ",Criterion->CriCod);
+ HTM_TD_End ();
+
+ /***** Write title *****/
+ HTM_TD_Begin ("class=\"LT %s\"",The_GetColorRows ());
+ HTM_ARTICLE_Begin (Anchor);
+ if (CriterionExists)
+ /* Write title */
+ Rub_WriteCriterionTitle (Criterion->Title,"Qst_TXT",
+ true); // Visible
+ else
+ {
+ HTM_SPAN_Begin ("class=\"DAT_LIGHT_%s\"",
+ The_GetSuffix ());
+ HTM_Txt (Txt_Criterion_removed);
+ HTM_SPAN_End ();
+ }
+ HTM_ARTICLE_End ();
+ HTM_TD_End ();
+ }
+
+/*****************************************************************************/
+/******************* Write the number of a rubric criterion ******************/
+/*****************************************************************************/
+// Number of criterion should be 1, 2, 3...
+
+void Rub_WriteNumCriterion (unsigned NumCri,const char *Class)
+ {
+ HTM_DIV_Begin ("class=\"%s_%s\"",Class,The_GetSuffix ());
+ HTM_Unsigned (NumCri);
+ HTM_DIV_End ();
+ }
+
+/*****************************************************************************/
+/******************* Write the title of a rubric criterion *******************/
+/*****************************************************************************/
+
+void Rub_WriteCriterionTitle (const char *Title,const char *ClassTitle,bool Visible)
+ {
+ unsigned long TitleLength;
+ char *TitleRigorousHTML;
+
+ /***** DIV begin *****/
+ HTM_DIV_Begin ("class=\"%s_%s\"",ClassTitle,The_GetSuffix ());
+
+ /***** Write title *****/
+ if (Title && Visible)
+ {
+ if (Title[0])
+ {
+ /* Convert the title, that is in HTML, to rigorous HTML */
+ TitleLength = strlen (Title) * Str_MAX_BYTES_PER_CHAR;
+ if ((TitleRigorousHTML = malloc (TitleLength + 1)) == NULL)
+ Err_NotEnoughMemoryExit ();
+ Str_Copy (TitleRigorousHTML,Title,TitleLength);
+
+ Str_ChangeFormat (Str_FROM_HTML,Str_TO_RIGOROUS_HTML,
+ TitleRigorousHTML,TitleLength,false);
+
+ /* Write title text */
+ HTM_Txt (TitleRigorousHTML);
+
+ /* Free memory allocated for the title */
+ free (TitleRigorousHTML);
+ }
+ }
+ else
+ Ico_PutIconNotVisible ();
+
+ /***** DIV end *****/
+ HTM_DIV_End ();
+ }
diff --git a/swad_rubric.h b/swad_rubric.h
new file mode 100644
index 00000000..9cebae29
--- /dev/null
+++ b/swad_rubric.h
@@ -0,0 +1,119 @@
+// swad_rubric.h: assessment rubrics
+
+#ifndef _SWAD_RUB
+#define _SWAD_RUB
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ 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-2022 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 "swad_date.h"
+#include "swad_scope.h"
+
+/*****************************************************************************/
+/************************** Public types and constants ***********************/
+/*****************************************************************************/
+
+#define Rub_MAX_CHARS_TITLE (128 - 1) // 127
+#define Rub_MAX_BYTES_TITLE ((Rub_MAX_CHARS_TITLE + 1) * Str_MAX_BYTES_PER_CHAR - 1) // 2047
+
+#define Rub_AFTER_LAST_CRITERION ((unsigned)((1UL << 31) - 1)) // 2^31 - 1, don't change this number because it is used in database
+
+struct Rub_Rubric
+ {
+ long RubCod; // Rubric code
+ long CrsCod; // Course code
+ long UsrCod; // Author code
+ char Title[Rub_MAX_BYTES_TITLE + 1];
+ unsigned NumCriteria; // Number of criteria in the rubric
+ };
+
+struct Rub_Rubrics
+ {
+ bool LstIsRead; // Is the list already read from database...
+ // ...or it needs to be read?
+ unsigned Num; // Total number of rubrics
+ long *Lst; // List of rubric codes
+ unsigned CurrentPage;
+ struct Rub_Rubric Rubric; // Selected/current rubric
+ };
+
+struct Rub_Criterion
+ {
+ long RubCod; // Rubric code
+ long CriCod; // Course code
+ char Title[Rub_MAX_BYTES_TITLE + 1];
+ };
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+void Rub_ResetRubrics (struct Rub_Rubrics *Rubrics);
+void Rub_ResetRubric (struct Rub_Rubric *Rubric);
+
+void Rub_SeeAllRubrics (void);
+void Rub_ListAllRubrics (struct Rub_Rubrics *Rubrics);
+void Rub_SeeOneRubric (void);
+void Rub_ShowOnlyOneRubric (struct Rub_Rubrics *Rubrics,
+ bool ListRubricDims);
+void Rub_ShowOnlyOneRubricBegin (struct Rub_Rubrics *Rubrics,
+ bool ListRubricDims);
+void Rub_ShowOnlyOneRubricEnd (void);
+
+void Rub_SetCurrentRubCod (long GamCod);
+void Rub_PutParams (void *Rubrics);
+void Rub_PutParamRubCod (long RubCod);
+long Rub_GetParamRubCod (void);
+long Rub_GetParams (struct Rub_Rubrics *Rubrics);
+
+void Rub_GetListRubrics (struct Rub_Rubrics *Rubrics);
+void Rub_GetDataOfRubricByCod (struct Rub_Rubric *Rubric);
+void Rub_GetDataOfRubricByFolder (struct Rub_Rubric *Rubric);
+void Rub_FreeListRubrics (struct Rub_Rubrics *Rubrics);
+
+void Rub_AskRemRubric (void);
+void Rub_RemoveRubric (void);
+void Rub_RemoveCrsRubrics (long CrsCod);
+
+void Rub_ListRubric (void);
+
+void Rub_RequestCreatOrEditRubric (void);
+
+void Rub_ReceiveFormRubric (void);
+
+//------------------------------- Criteria ------------------------------------
+void Rub_CriterionConstructor (struct Rub_Criterion *Criterion);
+void Rub_CriterionDestructor (struct Rub_Criterion *Criterion);
+void Rub_PutParamCriCod (void *CriCod);
+bool Rub_GetCriterionDataFromDB (struct Rub_Criterion *Criterion);
+void Rub_ListCriterionForEdition (struct Rub_Criterion *Criterion,
+ unsigned CriInd,bool CriterionExists,
+ const char *Anchor);
+void Rub_WriteNumCriterion (unsigned NumDim,const char *Class);
+void Rub_WriteCriterionTitle (const char *Title,const char *ClassTitle,bool Visible);
+
+//-------------------------------- Figures ------------------------------------
+void Rub_GetAndShowRubricsStats (void);
+
+#endif
diff --git a/swad_rubric_database.c b/swad_rubric_database.c
new file mode 100644
index 00000000..8c6a5e0d
--- /dev/null
+++ b/swad_rubric_database.c
@@ -0,0 +1,672 @@
+// swad_rubric_database.c: assessment rubrics, operations with database
+
+/*
+ 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-2022 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 ***********************************/
+/*****************************************************************************/
+
+#define _GNU_SOURCE // For asprintf
+#include // For asprintf
+#include // For free
+
+#include "swad_database.h"
+#include "swad_error.h"
+#include "swad_rubric.h"
+#include "swad_rubric_database.h"
+#include "swad_global.h"
+
+/*****************************************************************************/
+/************** External global variables from others modules ****************/
+/*****************************************************************************/
+
+extern struct Globals Gbl;
+
+/*****************************************************************************/
+/*************************** Create a new rubric *****************************/
+/*****************************************************************************/
+
+long Rub_DB_CreateRubric (const struct Rub_Rubric *Rubric,const char *Txt)
+ {
+ return
+ DB_QueryINSERTandReturnCode ("can not create new rubric",
+ "INSERT INTO rub_rubrics"
+ " (CrsCod,UsrCod,Title,Txt)"
+ " VALUES"
+ " (%ld,%ld,'%s','%s')",
+ Gbl.Hierarchy.Crs.CrsCod,
+ Gbl.Usrs.Me.UsrDat.UsrCod,
+ Rubric->Title,
+ Txt);
+ }
+
+/*****************************************************************************/
+/************************** Update an existing rubric ************************/
+/*****************************************************************************/
+
+void Rub_DB_UpdateRubric (const struct Rub_Rubric *Rubric,const char *Txt)
+ {
+ DB_QueryUPDATE ("can not update rubric",
+ "UPDATE rub_rubrics"
+ " SET CrsCod=%ld,"
+ "Title='%s',"
+ "Txt='%s'"
+ " WHERE RubCod=%ld",
+ Gbl.Hierarchy.Crs.CrsCod,
+ Rubric->Title,
+ Txt,
+ Rubric->RubCod);
+ }
+
+/*****************************************************************************/
+/************** Get list of all rubrics in the current course ****************/
+/*****************************************************************************/
+
+unsigned Rub_DB_GetListRubrics (MYSQL_RES **mysql_res)
+ {
+ return (unsigned)
+ DB_QuerySELECT (mysql_res,"can not get rubrics",
+ "SELECT RubCod" // row[0]
+ " FROM rub_rubrics"
+ " WHERE CrsCod=%ld"
+ " ORDER BY Title",
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/********************* Get rubric data using its code ************************/
+/*****************************************************************************/
+
+unsigned Rub_DB_GetDataOfRubricByCod (MYSQL_RES **mysql_res,long RubCod)
+ {
+ return (unsigned)
+ DB_QuerySELECT (mysql_res,"can not get rubric data",
+ "SELECT RubCod," // row[0]
+ "CrsCod," // row[1]
+ "UsrCod," // row[2]
+ "Title" // row[3]
+ " FROM rub_rubrics"
+ " WHERE RubCod=%ld"
+ " AND CrsCod=%ld", // Extra check
+ RubCod,
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/********************** Get rubric title from database ***********************/
+/*****************************************************************************/
+
+void Rub_DB_GetRubricTitle (long RubCod,char Title[Rub_MAX_BYTES_TITLE + 1])
+ {
+ DB_QuerySELECTString (Title,Rub_MAX_BYTES_TITLE,"can not get rubric title",
+ "SELECT Title" // row[0]
+ " FROM rub_rubrics"
+ " WHERE RubCod=%ld"
+ " AND CrsCod=%ld", // Extra check
+ RubCod,
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/********************** Get rubric text from database ************************/
+/*****************************************************************************/
+
+void Rub_DB_GetRubricTxt (long RubCod,char Txt[Cns_MAX_BYTES_TEXT + 1])
+ {
+ DB_QuerySELECTString (Txt,Cns_MAX_BYTES_TEXT,"can not get rubric text",
+ "SELECT Txt" // row[0]
+ " FROM rub_rubrics"
+ " WHERE RubCod=%ld",
+ RubCod);
+ }
+
+/*****************************************************************************/
+/******************* Check if the title of a rubric exists *******************/
+/*****************************************************************************/
+
+bool Rub_DB_CheckIfSimilarRubricExists (const struct Rub_Rubric *Rubric)
+ {
+ return
+ DB_QueryEXISTS ("can not check similar rubrics",
+ "SELECT EXISTS"
+ "(SELECT *"
+ " FROM rub_rubrics"
+ " WHERE CrsCod=%ld"
+ " AND Title='%s'"
+ " AND RubCod<>%ld)",
+ Gbl.Hierarchy.Crs.CrsCod,
+ Rubric->Title,
+ Rubric->RubCod);
+ }
+
+/*****************************************************************************/
+/******************** Get number of courses with rubrics *********************/
+/*****************************************************************************/
+// Returns the number of courses with rubrics in this location
+
+unsigned Rub_DB_GetNumCoursesWithRubrics (HieLvl_Level_t Scope)
+ {
+ /***** Get number of courses with rubrics from database *****/
+ switch (Scope)
+ {
+ case HieLvl_SYS:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of courses with rubrics",
+ "SELECT COUNT(DISTINCT CrsCod)"
+ " FROM rub_rubrics");
+ case HieLvl_CTY:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of courses with rubrics",
+ "SELECT COUNT(DISTINCT rub_rubrics.CrsCod)"
+ " FROM ins_instits,"
+ "ctr_centers,"
+ "deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics"
+ " WHERE ins_instits.CtyCod=%ld"
+ " AND ins_instits.InsCod=ctr_centers.InsCod"
+ " AND ctr_centers.CtrCod=deg_degrees.CtrCod"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Ins.InsCod);
+ case HieLvl_INS:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of courses with rubrics",
+ "SELECT COUNT(DISTINCT rub_rubrics.CrsCod)"
+ " FROM ctr_centers,"
+ "deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics"
+ " WHERE ctr_centers.InsCod=%ld"
+ " AND ctr_centers.CtrCod=deg_degrees.CtrCod"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Ins.InsCod);
+ case HieLvl_CTR:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of courses with rubrics",
+ "SELECT COUNT(DISTINCT rub_rubrics.CrsCod)"
+ " FROM deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics"
+ " WHERE deg_degrees.CtrCod=%ld"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Ctr.CtrCod);
+ case HieLvl_DEG:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of courses with rubrics",
+ "SELECT COUNT(DISTINCT rub_rubrics.CrsCod)"
+ " FROM crs_courses,"
+ "rub_rubrics"
+ " WHERE crs_courses.DegCod=%ld"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Deg.DegCod);
+ case HieLvl_CRS:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of courses with rubrics",
+ "SELECT COUNT(DISTINCT CrsCod)"
+ " FROM rub_rubrics"
+ " WHERE CrsCod=%ld",
+ Gbl.Hierarchy.Crs.CrsCod);
+ default:
+ return 0;
+ }
+ }
+
+/*****************************************************************************/
+/*************************** Get number of rubrics ***************************/
+/*****************************************************************************/
+// Returns the number of rubrics in this location
+
+unsigned Rub_DB_GetNumRubrics (HieLvl_Level_t Scope)
+ {
+ /***** Get number of rubrics from database *****/
+ switch (Scope)
+ {
+ case HieLvl_SYS:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of rubrics",
+ "SELECT COUNT(*)"
+ " FROM rub_rubrics");
+ case HieLvl_CTY:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of rubrics",
+ "SELECT COUNT(*)"
+ " FROM ins_instits,"
+ "ctr_centers,"
+ "deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics"
+ " WHERE ins_instits.CtyCod=%ld"
+ " AND ins_instits.InsCod=ctr_centers.InsCod"
+ " AND ctr_centers.CtrCod=deg_degrees.CtrCod"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Cty.CtyCod);
+ case HieLvl_INS:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of rubrics",
+ "SELECT COUNT(*)"
+ " FROM ctr_centers,"
+ "deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics"
+ " WHERE ctr_centers.InsCod=%ld"
+ " AND ctr_centers.CtrCod=deg_degrees.CtrCod"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Ins.InsCod);
+ case HieLvl_CTR:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of rubrics",
+ "SELECT COUNT(*)"
+ " FROM deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics"
+ " WHERE deg_degrees.CtrCod=%ld"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Ctr.CtrCod);
+ case HieLvl_DEG:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of rubrics",
+ "SELECT COUNT(*)"
+ " FROM crs_courses,"
+ "rub_rubrics"
+ " WHERE crs_courses.DegCod=%ld"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod",
+ Gbl.Hierarchy.Deg.DegCod);
+ case HieLvl_CRS:
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of rubrics",
+ "SELECT COUNT(*)"
+ " FROM rub_rubrics"
+ " WHERE CrsCod=%ld",
+ Gbl.Hierarchy.Crs.CrsCod);
+ default:
+ return 0;
+ }
+ }
+
+/*****************************************************************************/
+/******************************** Remove rubric ******************************/
+/*****************************************************************************/
+
+void Rub_DB_RemoveRubric (long RubCod)
+ {
+ DB_QueryDELETE ("can not remove rubric",
+ "DELETE FROM rub_rubrics"
+ " WHERE RubCod=%ld",
+ RubCod);
+ }
+
+/*****************************************************************************/
+/********************** Remove the rubrics of a course ***********************/
+/*****************************************************************************/
+
+void Rub_DB_RemoveCrsRubrics (long CrsCod)
+ {
+ DB_QueryDELETE ("can not remove course rubrics",
+ "DELETE FROM rub_rubrics"
+ " WHERE CrsCod=%ld",
+ CrsCod);
+ }
+
+/*****************************************************************************/
+/**************** Insert criterion in the table of criteria *****************/
+/*****************************************************************************/
+
+void Rub_DB_InsertCriterionInRubric (long RubCod,unsigned CriInd,long CriCod)
+ {
+ DB_QueryINSERT ("can not add criterion to rubric",
+ "INSERT INTO rub_criteria"
+ " (RubCod,CriInd,CriCod)"
+ " VALUES"
+ " (%ld,%u,%ld)",
+ RubCod,
+ CriInd,
+ CriCod);
+ }
+
+/*****************************************************************************/
+/*********** Update indexes of criteria greater than a given one ***********/
+/*****************************************************************************/
+
+void Rub_DB_UpdateIndexesOfCriteriaGreaterThan (long RubCod,unsigned CriInd)
+ {
+ DB_QueryUPDATE ("can not update indexes of criteria",
+ "UPDATE rub_criteria"
+ " SET CriInd=CriInd-1"
+ " WHERE RubCod=%ld"
+ " AND CriInd>%u",
+ RubCod,
+ CriInd);
+ }
+
+/*****************************************************************************/
+/****************** Change index of a criterion in a rubric ******************/
+/*****************************************************************************/
+
+void Rub_DB_UpdateCriterionIndex (long CriInd,long RubCod,long CriCod)
+ {
+ DB_QueryUPDATE ("can not exchange indexes of criteria",
+ "UPDATE rub_criteria"
+ " SET CriInd=%ld"
+ " WHERE RubCod=%ld"
+ " AND CriCod=%ld",
+ CriInd,
+ RubCod,
+ CriCod);
+ }
+
+/*****************************************************************************/
+/************ Lock table to make the exchange of criteria atomic ***********/
+/*****************************************************************************/
+
+void Rub_DB_LockTable (void)
+ {
+ DB_Query ("can not lock tables to move rubric criterion",
+ "LOCK TABLES rub_criteria WRITE");
+ DB_SetThereAreLockedTables ();
+ }
+
+/*****************************************************************************/
+/******************* Get number of criteria of a rubric *********************/
+/*****************************************************************************/
+
+unsigned Rub_DB_GetNumCriteriaInRubric (long RubCod)
+ {
+ /***** Get nuumber of criteria in a rubric from database *****/
+ return (unsigned)
+ DB_QueryCOUNT ("can not get number of criteria of a rubric",
+ "SELECT COUNT(*)"
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld",
+ RubCod);
+ }
+
+/*****************************************************************************/
+/*********************** Get the criteria of a rubric ***********************/
+/*****************************************************************************/
+
+unsigned Rub_DB_GetRubricCriteriaBasic (MYSQL_RES **mysql_res,long RubCod)
+ {
+ return (unsigned)
+ DB_QuerySELECT (mysql_res,"can not get rubric criteria",
+ "SELECT CriCod," // row[0]
+ "CriInd" // row[1]
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld"
+ " ORDER BY CriInd",
+ RubCod);
+ }
+
+/*****************************************************************************/
+/********* Get criterion code given rubric and index of criterion ************/
+/*****************************************************************************/
+
+long Rub_DB_GetCriCodFromCriInd (long RubCod,unsigned CriInd)
+ {
+ long CriCod;
+
+ CriCod = DB_QuerySELECTCode ("can not get criterion code",
+ "SELECT CriCod"
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld"
+ " AND CriInd=%u",
+ RubCod,
+ CriInd);
+ if (CriCod <= 0)
+ Err_WrongCriterionExit ();
+
+ return CriCod;
+ }
+
+/*****************************************************************************/
+/********** Get criterion index given rubric and code of criterion ***********/
+/*****************************************************************************/
+// Return 0 is criterion is not present in rubric
+
+unsigned Rub_DB_GetCriIndFromCriCod (long RubCod,long CriCod)
+ {
+ return DB_QuerySELECTUnsigned ("can not get criterion index",
+ "SELECT CriInd"
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld"
+ " AND CriCod=%ld",
+ RubCod,
+ CriCod);
+ }
+
+/*****************************************************************************/
+/**************** Get maximum criterion index in a rubric ********************/
+/*****************************************************************************/
+// Criterion index can be 1, 2, 3...
+// Return 0 if no criteria
+
+unsigned Rub_DB_GetMaxCriterionIndexInRubric (long RubCod)
+ {
+ /***** Get maximum criterion index in a rubric from database *****/
+ return DB_QuerySELECTUnsigned ("can not get last criterion index",
+ "SELECT MAX(CriInd)"
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld",
+ RubCod);
+ }
+
+/*****************************************************************************/
+/********* Get previous criterion index to a given index in a rubric *********/
+/*****************************************************************************/
+// Input criterion index can be 1, 2, 3... n-1
+// Return criterion index will be 1, 2, 3... n if previous criterion exists, or 0 if no previous criterion
+
+unsigned Rub_DB_GetPrevCriterionIndexInRubric (long RubCod,unsigned CriInd)
+ {
+ /***** Get previous criterion index in a rubric from database *****/
+ // Although indexes are always continuous...
+ // ...this implementation works even with non continuous indexes
+ return DB_QuerySELECTUnsigned ("can not get previous criterion index",
+ "SELECT COALESCE(MAX(CriInd),0)"
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld"
+ " AND CriInd<%u",
+ RubCod,
+ CriInd);
+ }
+
+/*****************************************************************************/
+/*********** Get next criterion index to a given index in a rubric ***********/
+/*****************************************************************************/
+// Input criterion index can be 0, 1, 2, 3... n-1
+// Return criterion index will be 1, 2, 3... n if next criterion exists, or big number if no next criterion
+
+unsigned Rub_DB_GetNextCriterionIndexInRubric (long RubCod,unsigned CriInd)
+ {
+ /***** Get next criterion index in a rubric from database *****/
+ // Although indexes are always continuous...
+ // ...this implementation works even with non continuous indexes
+ return DB_QuerySELECTUnsigned ("can not get next criterion index",
+ "SELECT COALESCE(MIN(CriInd),%u)"
+ " FROM rub_criteria"
+ " WHERE RubCod=%ld"
+ " AND CriInd>%u",
+ Rub_AFTER_LAST_CRITERION, // End of criteria has been reached
+ RubCod,
+ CriInd);
+ }
+
+/*****************************************************************************/
+/****************** Get data of a criterion from database ********************/
+/*****************************************************************************/
+
+unsigned Rub_DB_GetCriterionData (MYSQL_RES **mysql_res,long CriCod)
+ {
+ return (unsigned)
+ DB_QuerySELECT (mysql_res,"can not get a criterion",
+ "SELECT Title" // row[0]
+ " FROM rub_criteria"
+ " WHERE QstCod=%ld"
+ " AND CrsCod=%ld", // Extra check
+ CriCod,
+ Gbl.Hierarchy.Crs.CrsCod);
+ }
+
+/*****************************************************************************/
+/**************** Get average number of criteria per rubric ******************/
+/*****************************************************************************/
+
+double Rub_DB_GetNumCriteriaPerRubric (HieLvl_Level_t Scope)
+ {
+ /***** Get number of criteria per rubric from database *****/
+ switch (Scope)
+ {
+ case HieLvl_SYS:
+ return
+ DB_QuerySELECTDouble ("can not get number of criteria per rubric",
+ "SELECT AVG(NumCriteria)"
+ " FROM (SELECT COUNT(rub_criteria.QstCod) AS NumCriteria"
+ " FROM rub_rubrics,"
+ "rub_criteria"
+ " WHERE rub_rubrics.RubCod=rub_criteria.RubCod"
+ " GROUP BY rub_criteria.RubCod) AS NumCriteriaTable");
+ case HieLvl_CTY:
+ return
+ DB_QuerySELECTDouble ("can not get number of criteria per rubric",
+ "SELECT AVG(NumCriteria)"
+ " FROM (SELECT COUNT(rub_criteria.QstCod) AS NumCriteria"
+ " FROM ins_instits,"
+ "ctr_centers,"
+ "deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics,"
+ "rub_criteria"
+ " WHERE ins_instits.CtyCod=%ld"
+ " AND ins_instits.InsCod=ctr_centers.InsCod"
+ " AND ctr_centers.CtrCod=deg_degrees.CtrCod"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod"
+ " AND rub_rubrics.RubCod=rub_criteria.RubCod"
+ " GROUP BY rub_criteria.RubCod) AS NumCriteriaTable",
+ Gbl.Hierarchy.Cty.CtyCod);
+ case HieLvl_INS:
+ return
+ DB_QuerySELECTDouble ("can not get number of criteria per rubric",
+ "SELECT AVG(NumCriteria)"
+ " FROM (SELECT COUNT(rub_criteria.QstCod) AS NumCriteria"
+ " FROM ctr_centers,"
+ "deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics,"
+ "rub_criteria"
+ " WHERE ctr_centers.InsCod=%ld"
+ " AND ctr_centers.CtrCod=deg_degrees.CtrCod"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod"
+ " AND rub_rubrics.RubCod=rub_criteria.RubCod"
+ " GROUP BY rub_criteria.RubCod) AS NumCriteriaTable",
+ Gbl.Hierarchy.Ins.InsCod);
+ case HieLvl_CTR:
+ return
+ DB_QuerySELECTDouble ("can not get number of criteria per rubric",
+ "SELECT AVG(NumCriteria)"
+ " FROM (SELECT COUNT(rub_criteria.QstCod) AS NumCriteria"
+ " FROM deg_degrees,"
+ "crs_courses,"
+ "rub_rubrics,"
+ "rub_criteria"
+ " WHERE deg_degrees.CtrCod=%ld"
+ " AND deg_degrees.DegCod=crs_courses.DegCod"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod"
+ " AND rub_rubrics.RubCod=rub_criteria.RubCod"
+ " GROUP BY rub_criteria.RubCod) AS NumCriteriaTable",
+ Gbl.Hierarchy.Ctr.CtrCod);
+ case HieLvl_DEG:
+ return
+ DB_QuerySELECTDouble ("can not get number of criteria per rubric",
+ "SELECT AVG(NumCriteria)"
+ " FROM (SELECT COUNT(rub_criteria.QstCod) AS NumCriteria"
+ " FROM crs_courses,"
+ "rub_rubrics,"
+ "rub_criteria"
+ " WHERE crs_courses.DegCod=%ld"
+ " AND crs_courses.CrsCod=rub_rubrics.CrsCod"
+ " AND rub_rubrics.RubCod=rub_criteria.RubCod"
+ " GROUP BY rub_criteria.RubCod) AS NumCriteriaTable",
+ Gbl.Hierarchy.Deg.DegCod);
+ case HieLvl_CRS:
+ return
+ DB_QuerySELECTDouble ("can not get number of criteria per rubric",
+ "SELECT AVG(NumCriteria)"
+ " FROM (SELECT COUNT(rub_criteria.QstCod) AS NumCriteria"
+ " FROM rub_rubrics,"
+ "rub_criteria"
+ " WHERE rub_rubrics.Cod=%ld"
+ " AND rub_rubrics.RubCod=rub_criteria.RubCod"
+ " GROUP BY rub_criteria.RubCod) AS NumCriteriaTable",
+ Gbl.Hierarchy.Crs.CrsCod);
+ default:
+ Err_WrongScopeExit ();
+ return 0.0; // Not reached
+ }
+ }
+
+/*****************************************************************************/
+/*********************** Remove criterion from a rubric **********************/
+/*****************************************************************************/
+
+void Rub_DB_RemoveCriterionFromRubric (long RubCod,unsigned CriInd)
+ {
+ DB_QueryDELETE ("can not remove a criterion",
+ "DELETE FROM rub_criteria"
+ " WHERE RubCod=%ld"
+ " AND CriInd=%u",
+ RubCod,
+ CriInd);
+ }
+
+/*****************************************************************************/
+/**************************** Remove rubric criteria *************************/
+/*****************************************************************************/
+
+void Rub_DB_RemoveRubricCriteria (long RubCod)
+ {
+ DB_QueryDELETE ("can not remove rubric criteria",
+ "DELETE FROM rub_criteria"
+ " WHERE RubCod=%ld",
+ RubCod);
+ }
+
+/*****************************************************************************/
+/**************** Remove the criteria in rubrics of a course *****************/
+/*****************************************************************************/
+
+void Rub_DB_RemoveCrsRubricCriteria (long CrsCod)
+ {
+ DB_QueryDELETE ("can not remove criteria in course rubrics",
+ "DELETE FROM rub_criteria"
+ " USING rub_rubrics,"
+ "rub_criteria"
+ " WHERE rub_rubrics.CrsCod=%ld"
+ " AND rub_rubrics.RubCod=rub_criteria.RubCod",
+ CrsCod);
+ }
diff --git a/swad_rubric_database.h b/swad_rubric_database.h
new file mode 100644
index 00000000..b098f5b6
--- /dev/null
+++ b/swad_rubric_database.h
@@ -0,0 +1,75 @@
+// swad_rubric_database.h: assessment rubrics, operations with database
+
+#ifndef _SWAD_RUB_DB
+#define _SWAD_RUB_DB
+/*
+ SWAD (Shared Workspace At a Distance in Spanish),
+ 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-2022 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 // To access MySQL databases
+
+#include "swad_game.h"
+#include "swad_hierarchy_level.h"
+
+/*****************************************************************************/
+/***************************** Public prototypes *****************************/
+/*****************************************************************************/
+
+//-------------------------------- Rubrics ------------------------------------
+long Rub_DB_CreateRubric (const struct Rub_Rubric *Rubric,const char *Txt);
+void Rub_DB_UpdateRubric (const struct Rub_Rubric *Rubric,const char *Txt);
+void Rub_DB_HideOrUnhideRubric (long RubCod,bool Hide);
+
+unsigned Rub_DB_GetListRubrics (MYSQL_RES **mysql_res);
+unsigned Rub_DB_GetDataOfRubricByCod (MYSQL_RES **mysql_res,long RubCod);
+void Rub_DB_GetRubricTitle (long RubCod,char Title[Rub_MAX_BYTES_TITLE + 1]);
+void Rub_DB_GetRubricTxt (long RubCod,char Txt[Cns_MAX_BYTES_TEXT + 1]);
+bool Rub_DB_CheckIfSimilarRubricExists (const struct Rub_Rubric *Rubric);
+unsigned Rub_DB_GetNumCoursesWithRubrics (HieLvl_Level_t Scope);
+unsigned Rub_DB_GetNumRubrics (HieLvl_Level_t Scope);
+
+void Rub_DB_RemoveRubric (long RubCod);
+void Rub_DB_RemoveCrsRubrics (long CrsCod);
+
+//--------------------------- Rubric criteria -------------------------------
+void Rub_DB_InsertCriterionInRubric (long RubCod,unsigned CriInd,long CriCod);
+void Rub_DB_UpdateIndexesOfCriteriaGreaterThan (long RubCod,unsigned CriInd);
+void Rub_DB_UpdateCriterionIndex (long CriInd,long RubCod,long CriCod);
+void Rub_DB_LockTable (void);
+
+unsigned Rub_DB_GetNumCriteriaInRubric (long RubCod);
+unsigned Rub_DB_GetRubricCriteriaBasic (MYSQL_RES **mysql_res,long RubCod);
+long Rub_DB_GetCriCodFromCriInd (long RubCod,unsigned QstInd);
+unsigned Rub_DB_GetCriIndFromCriCod (long RubCod,long QstCod);
+unsigned Rub_DB_GetMaxCriterionIndexInRubric (long RubCod);
+unsigned Rub_DB_GetPrevCriterionIndexInRubric (long RubCod,unsigned QstInd);
+unsigned Rub_DB_GetNextCriterionIndexInRubric (long RubCod,unsigned QstInd);
+unsigned Rub_DB_GetCriterionData (MYSQL_RES **mysql_res,long CriCod);
+double Rub_DB_GetNumCriteriaPerRubric (HieLvl_Level_t Scope);
+
+void Rub_DB_RemoveCriterionFromRubric (long RubCod,unsigned CriInd);
+void Rub_DB_RemoveRubricCriteria (long RubCod);
+void Rub_DB_RemoveCrsRubricCriteria (long CrsCod);
+
+#endif