From a62b8fc90e60af537001df51ccdce59aa027b5c2 Mon Sep 17 00:00:00 2001 From: acanas Date: Sat, 2 May 2020 13:39:59 +0200 Subject: [PATCH] Version19.209.5 --- swad_centre.c | 25 +++++++++++++------ swad_changelog.h | 3 ++- swad_country.c | 47 +++++++++++++++++++++++++++++------ swad_country_config.c | 12 ++++++++- swad_course.c | 4 +-- swad_degree.c | 58 ++++++++++++++++++++++++++++++++++++++----- swad_figure_cache.c | 20 +++++++++++---- swad_figure_cache.h | 2 ++ swad_institution.c | 18 +++++++++++--- 9 files changed, 155 insertions(+), 34 deletions(-) diff --git a/swad_centre.c b/swad_centre.c index c8f63751..9d67111d 100644 --- a/swad_centre.c +++ b/swad_centre.c @@ -34,6 +34,7 @@ #include "swad_centre_config.h" #include "swad_database.h" #include "swad_figure.h" +#include "swad_figure_cache.h" #include "swad_form.h" #include "swad_forum.h" #include "swad_global.h" @@ -360,6 +361,7 @@ static void Ctr_ListOneCentreForSeeing (struct Centre *Ctr,unsigned NumCtr) const char *TxtClassNormal; const char *TxtClassStrong; const char *BgColor; + unsigned NumUsrsInCrss; Ctr_StatusTxt_t StatusTxt; /***** Get data of place of this centre *****/ @@ -414,10 +416,18 @@ static void Ctr_ListOneCentreForSeeing (struct Centre *Ctr,unsigned NumCtr) /***** Number of users in courses of this centre *****/ HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); - HTM_Unsigned (Usr_GetNumUsrsInCrss (Hie_CTR,Ctr->CtrCod, - 1 << Rol_STD | - 1 << Rol_NET | - 1 << Rol_TCH)); // Any user + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_IN_CRSS,Hie_CTR,Ctr->CtrCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsInCrss = Usr_GetNumUsrsInCrss (Hie_CTR,Ctr->CtrCod, + 1 << Rol_STD | + 1 << Rol_NET | + 1 << Rol_TCH); // Any user + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_IN_CRSS,Hie_CTR,Ctr->CtrCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss); + } + HTM_Unsigned (NumUsrsInCrss); HTM_TD_End (); /***** Centre status *****/ @@ -939,6 +949,7 @@ static void Ctr_ListCentresForEdition (const struct Plc_Places *Places) char WWW[Cns_MAX_BYTES_WWW + 1]; struct UsrData UsrDat; bool ICanEdit; + unsigned NumDegs; unsigned NumUsrsInCrssOfCtr; Ctr_StatusTxt_t StatusTxt; unsigned StatusUnsigned; @@ -958,6 +969,7 @@ static void Ctr_ListCentresForEdition (const struct Plc_Places *Places) Ctr = &Gbl.Hierarchy.Ctrs.Lst[NumCtr]; ICanEdit = Ctr_CheckIfICanEditACentre (Ctr); + NumDegs = Deg_GetNumDegsInCtr (Ctr->CtrCod); NumUsrsInCrssOfCtr = Usr_GetNumUsrsInCrss (Hie_CTR,Ctr->CtrCod, 1 << Rol_STD | 1 << Rol_NET | @@ -967,10 +979,9 @@ static void Ctr_ListCentresForEdition (const struct Plc_Places *Places) HTM_TR_Begin (NULL); HTM_TD_Begin ("class=\"BM\""); if (!ICanEdit || // I cannot edit + NumDegs || // Centre has degrees NumUsrsInCrssOfCtr) // Centre has users Ico_PutIconRemovalNotAllowed (); - else if (Deg_GetNumDegsInCtr (Ctr->CtrCod)) // Centre has degrees - Ico_PutIconRemovalNotAllowed (); else if (Usr_GetNumUsrsWhoClaimToBelongToCtr (Ctr)) // Centre has users who claim to belong to it Ico_PutIconRemovalNotAllowed (); else // I can remove centre @@ -1080,7 +1091,7 @@ static void Ctr_ListCentresForEdition (const struct Plc_Places *Places) /* Number of degrees */ HTM_TD_Begin ("class=\"DAT RM\""); - HTM_Unsigned (Deg_GetNumDegsInCtr (Ctr->CtrCod)); + HTM_Unsigned (NumDegs); HTM_TD_End (); /* Number of users in courses of this centre */ diff --git a/swad_changelog.h b/swad_changelog.h index 87b2307b..b0d76c79 100644 --- a/swad_changelog.h +++ b/swad_changelog.h @@ -544,10 +544,11 @@ enscript -2 --landscape --color --file-align=2 --highlight --line-numbers -o - * En OpenSWAD: ps2pdf source.ps destination.pdf */ -#define Log_PLATFORM_VERSION "SWAD 19.209.4 (2020-05-02)" +#define Log_PLATFORM_VERSION "SWAD 19.209.5 (2020-05-02)" #define CSS_FILE "swad19.193.1.css" #define JS_FILE "swad19.193.1.js" /* + Version 19.209.5: May 02, 2020 More figures cached. (300756 lines) Version 19.209.4: May 02, 2020 More figures cached. Code refactoring in hierarchy configuration. (300637 lines) Version 19.209.3: May 01, 2020 More figures cached. (300750 lines) diff --git a/swad_country.c b/swad_country.c index 1e4fc86f..64481a2b 100644 --- a/swad_country.c +++ b/swad_country.c @@ -35,6 +35,7 @@ #include "swad_country_config.h" #include "swad_database.h" #include "swad_figure.h" +#include "swad_figure_cache.h" #include "swad_form.h" #include "swad_global.h" #include "swad_HTML.h" @@ -227,6 +228,7 @@ void Cty_ListCountries2 (void) extern const char *Txt_Other_countries; extern const char *Txt_Country_unspecified; unsigned NumCty; + unsigned NumUsrsInCrss; /***** Write menu to select country *****/ Hie_WriteMenuHierarchy (); @@ -282,10 +284,20 @@ void Cty_ListCountries2 (void) HTM_Unsigned (Crs_GetNumCrssInCty (0)); HTM_TD_End (); + /* Number of users in courses of other countries */ HTM_TD_Begin ("class=\"DAT RM\""); - HTM_Unsigned (Usr_GetNumUsrsInCrss (Hie_CTY,0, - 1 << Rol_NET | // Non-editing teachers - 1 << Rol_TCH)); // Teachers + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_IN_CRSS,Hie_CTY,0, + FigCch_Type_UNSIGNED,&NumUsrsInCrss)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsInCrss = Usr_GetNumUsrsInCrss (Hie_CTY,0, + 1 << Rol_STD | + 1 << Rol_NET | + 1 << Rol_TCH); // Any user + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_IN_CRSS,Hie_CTY,0, + FigCch_Type_UNSIGNED,&NumUsrsInCrss); + } + HTM_Unsigned (NumUsrsInCrss); HTM_TD_End (); HTM_TR_End (); @@ -403,6 +415,8 @@ static void Cty_PutHeadCountriesForSeeing (bool OrderSelectable) static void Cty_ListOneCountryForSeeing (struct Country *Cty,unsigned NumCty) { const char *BgColor; + unsigned NumUsrsCty; + unsigned NumUsrsInCrss; BgColor = (Cty->CtyCod == Gbl.Hierarchy.Cty.CtyCod) ? "LIGHT_BLUE" : Gbl.ColorRows[Gbl.RowEvenOdd]; @@ -424,7 +438,15 @@ static void Cty_ListOneCountryForSeeing (struct Country *Cty,unsigned NumCty) /***** Number of users who claim to belong to this country *****/ HTM_TD_Begin ("class=\"DAT RM %s\"",BgColor); - HTM_Unsigned (Usr_GetNumUsrsWhoClaimToBelongToCty (Cty)); + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_CTY,Hie_CTY,Cty->CtyCod, + FigCch_Type_UNSIGNED,&NumUsrsCty)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsCty = Usr_GetNumUsrsWhoClaimToBelongToCty (Cty); + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_CTY,Hie_CTY,Cty->CtyCod, + FigCch_Type_UNSIGNED,&NumUsrsCty); + } + HTM_Unsigned (NumUsrsCty); HTM_TD_End (); /***** Other stats *****/ @@ -444,11 +466,20 @@ static void Cty_ListOneCountryForSeeing (struct Country *Cty,unsigned NumCty) HTM_Unsigned (Crs_GetNumCrssInCty (Cty->CtyCod)); HTM_TD_End (); + /* Number of users in courses */ HTM_TD_Begin ("class=\"DAT RM %s\"",BgColor); - HTM_Unsigned (Usr_GetNumUsrsInCrss (Hie_CTY,Cty->CtyCod, - 1 << Rol_STD | - 1 << Rol_NET | - 1 << Rol_TCH)); // Any user + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_IN_CRSS,Hie_CTY,Cty->CtyCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsInCrss = Usr_GetNumUsrsInCrss (Hie_CTY,Cty->CtyCod, + 1 << Rol_STD | + 1 << Rol_NET | + 1 << Rol_TCH); // Any user + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_IN_CRSS,Hie_CTY,Cty->CtyCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss); + } + HTM_Unsigned (NumUsrsInCrss); HTM_TD_End (); HTM_TR_End (); diff --git a/swad_country_config.c b/swad_country_config.c index 49c96011..791ad9b4 100644 --- a/swad_country_config.c +++ b/swad_country_config.c @@ -33,6 +33,7 @@ #include // For string functions #include "swad_database.h" +#include "swad_figure_cache.h" #include "swad_form.h" #include "swad_global.h" #include "swad_help.h" @@ -474,6 +475,7 @@ static void CtyCfg_QR (void) static void CtyCfg_NumUsrs (void) { extern const char *Txt_Users_of_the_country; + unsigned NumUsrsCty; /***** Number of users *****/ HTM_TR_Begin (NULL); @@ -483,7 +485,15 @@ static void CtyCfg_NumUsrs (void) /* Data */ HTM_TD_Begin ("class=\"DAT LB\""); - HTM_Unsigned (Usr_GetNumUsrsWhoClaimToBelongToCty (&Gbl.Hierarchy.Cty)); + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_CTY,Hie_CTY,Gbl.Hierarchy.Cty.CtyCod, + FigCch_Type_UNSIGNED,&NumUsrsCty)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsCty = Usr_GetNumUsrsWhoClaimToBelongToCty (&Gbl.Hierarchy.Cty); + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_CTY,Hie_CTY,Gbl.Hierarchy.Cty.CtyCod, + FigCch_Type_UNSIGNED,&NumUsrsCty); + } + HTM_Unsigned (NumUsrsCty); HTM_TD_End (); HTM_TR_End (); diff --git a/swad_course.c b/swad_course.c index 5cb71096..4885f0f9 100644 --- a/swad_course.c +++ b/swad_course.c @@ -965,13 +965,13 @@ static bool Crs_ListCoursesOfAYearForSeeing (unsigned Year) Frm_EndForm (); HTM_TD_End (); - /* Current number of teachers in this course */ + /* Number of teachers in this course */ HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); HTM_Unsigned (NumUsrs[Rol_TCH] + NumUsrs[Rol_NET]); HTM_TD_End (); - /* Current number of students in this course */ + /* Number of students in this course */ HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); HTM_Unsigned (NumUsrs[Rol_STD]); HTM_TD_End (); diff --git a/swad_degree.c b/swad_degree.c index 00594c18..c8dd5c4c 100644 --- a/swad_degree.c +++ b/swad_degree.c @@ -34,6 +34,7 @@ #include "swad_degree.h" #include "swad_degree_config.h" #include "swad_figure.h" +#include "swad_figure_cache.h" #include "swad_form.h" #include "swad_forum.h" #include "swad_global.h" @@ -342,9 +343,10 @@ static void Deg_ListDegreesForEdition (void) char WWW[Cns_MAX_BYTES_WWW + 1]; struct UsrData UsrDat; bool ICanEdit; + unsigned NumCrss; + unsigned NumUsrsInCrssOfDeg; Deg_StatusTxt_t StatusTxt; unsigned StatusUnsigned; - unsigned NumCrss; /***** Initialize structure with user's data *****/ Usr_UsrDataConstructor (&UsrDat); @@ -360,15 +362,20 @@ static void Deg_ListDegreesForEdition (void) { Deg = &(Gbl.Hierarchy.Degs.Lst[NumDeg]); - NumCrss = Crs_GetNumCrssInDeg (Deg->DegCod); ICanEdit = Deg_CheckIfICanEditADegree (Deg); + NumCrss = Crs_GetNumCrssInDeg (Deg->DegCod); + NumUsrsInCrssOfDeg = Usr_GetNumUsrsInCrss (Hie_DEG,Deg->DegCod, + 1 << Rol_STD | + 1 << Rol_NET | + 1 << Rol_TCH); // Any user HTM_TR_Begin (NULL); /* Put icon to remove degree */ HTM_TD_Begin ("class=\"BM\""); - if (NumCrss || // Degree has courses ==> deletion forbidden - !ICanEdit) + if (!ICanEdit || + NumCrss || // Degree has courses ==> deletion forbidden + NumUsrsInCrssOfDeg) Ico_PutIconRemovalNotAllowed (); else { @@ -471,11 +478,16 @@ static void Deg_ListDegreesForEdition (void) } HTM_TD_End (); - /* Current number of courses in this degree */ + /* Number of courses in this degree */ HTM_TD_Begin ("class=\"DAT RM\""); HTM_Unsigned (NumCrss); HTM_TD_End (); + /* Number of users in courses of this degree */ + HTM_TD_Begin ("class=\"DAT RM\""); + HTM_Unsigned (NumUsrsInCrssOfDeg); + HTM_TD_End (); + /* Degree requester */ UsrDat.UsrCod = Deg->RequesterUsrCod; Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&UsrDat,Usr_DONT_GET_PREFS); @@ -646,7 +658,12 @@ static void Deg_PutFormToCreateDegree (void) "class=\"INPUT_WWW_NARROW\" required=\"required\""); HTM_TD_End (); - /***** Current number of courses in this degree *****/ + /***** Number of courses in this degree *****/ + HTM_TD_Begin ("class=\"DAT RM\""); + HTM_Unsigned (0); + HTM_TD_End (); + + /***** Number of users in courses of this degree *****/ HTM_TD_Begin ("class=\"DAT RM\""); HTM_Unsigned (0); HTM_TD_End (); @@ -678,6 +695,7 @@ static void Deg_PutHeadDegreesForSeeing (void) extern const char *Txt_Degree; extern const char *Txt_Type; extern const char *Txt_Courses_ABBREVIATION; + extern const char *Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NUM_ROLES]; HTM_TR_Begin (NULL); @@ -686,6 +704,11 @@ static void Deg_PutHeadDegreesForSeeing (void) HTM_TH (1,1,"LM",Txt_Degree); HTM_TH (1,1,"LM",Txt_Type); HTM_TH (1,1,"RM",Txt_Courses_ABBREVIATION); + HTM_TH_Begin (1,1,"RM"); + HTM_TxtF ("%s+",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_TCH]); + HTM_BR (); + HTM_Txt (Txt_ROLES_PLURAL_BRIEF_Abc[Rol_STD]); + HTM_TH_End (); HTM_TH_Empty (1); HTM_TR_End (); @@ -703,6 +726,7 @@ static void Deg_PutHeadDegreesForEdition (void) extern const char *Txt_Type; extern const char *Txt_WWW; extern const char *Txt_Courses_ABBREVIATION; + extern const char *Txt_ROLES_PLURAL_BRIEF_Abc[Rol_NUM_ROLES]; extern const char *Txt_Requester; HTM_TR_Begin (NULL); @@ -715,6 +739,11 @@ static void Deg_PutHeadDegreesForEdition (void) HTM_TH (1,1,"LM",Txt_Type); HTM_TH (1,1,"LM",Txt_WWW); HTM_TH (1,1,"RM",Txt_Courses_ABBREVIATION); + HTM_TH_Begin (1,1,"RM"); + HTM_TxtF ("%s+",Txt_ROLES_PLURAL_BRIEF_Abc[Rol_TCH]); + HTM_BR (); + HTM_Txt (Txt_ROLES_PLURAL_BRIEF_Abc[Rol_STD]); + HTM_TH_End (); HTM_TH (1,1,"LM",Txt_Requester); HTM_TH_Empty (1); @@ -860,6 +889,7 @@ static void Deg_ListOneDegreeForSeeing (struct Degree *Deg,unsigned NumDeg) const char *TxtClassStrong; const char *BgColor; unsigned NumCrss = Crs_GetNumCrssInDeg (Deg->DegCod); + unsigned NumUsrsInCrss; Deg_StatusTxt_t StatusTxt; /***** Get data of type of degree of this degree *****/ @@ -912,6 +942,22 @@ static void Deg_ListOneDegreeForSeeing (struct Degree *Deg,unsigned NumDeg) HTM_Unsigned (NumCrss); HTM_TD_End (); + /***** Number of users in courses of this degree *****/ + HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_IN_CRSS,Hie_DEG,Deg->DegCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsInCrss = Usr_GetNumUsrsInCrss (Hie_DEG,Deg->DegCod, + 1 << Rol_STD | + 1 << Rol_NET | + 1 << Rol_TCH); // Any user + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_IN_CRSS,Hie_DEG,Deg->DegCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss); + } + HTM_Unsigned (NumUsrsInCrss); + HTM_TD_End (); + /***** Degree status *****/ StatusTxt = Deg_GetStatusTxtFromStatusBits (Deg->Status); HTM_TD_Begin ("class=\"%s LM %s\"",TxtClassNormal,BgColor); diff --git a/swad_figure_cache.c b/swad_figure_cache.c index d475d887..acbb4cfd 100644 --- a/swad_figure_cache.c +++ b/swad_figure_cache.c @@ -41,8 +41,6 @@ /***************************** Private constants *****************************/ /*****************************************************************************/ -#define FigCch_TIME_CACHE ((time_t)(1UL * 60UL * 60UL)) // Past these seconds, update cached value - /*****************************************************************************/ /******************************* Private types *******************************/ /*****************************************************************************/ @@ -102,6 +100,17 @@ bool FigCch_GetFigureFromCache (FigCch_FigureCached_t Figure, Hie_Level_t Scope,long Cod, FigCch_Type_t Type,void *ValuePtr) { + /* The higher the level, the longer a value remains cached */ + time_t TimeCached[Hie_NUM_LEVELS] = // Time in seconds + { + [Hie_UNK] = (time_t) ( 0), // Unknown + [Hie_SYS] = (time_t) ( 1UL * 60UL * 60UL), // System + [Hie_CTY] = (time_t) ( 30UL * 60UL), // Country + [Hie_INS] = (time_t) ( 15UL * 60UL), // Institution + [Hie_CTR] = (time_t) ( 5UL * 60UL), // Centre + [Hie_DEG] = (time_t) ( 60UL), // Degree + [Hie_CRS] = (time_t) ( 10UL), // Course + }; static const char *Field[FigCch_NUM_TYPES] = { [FigCch_Type_UNSIGNED] = "ValueInt", @@ -123,8 +132,9 @@ bool FigCch_GetFigureFromCache (FigCch_FigureCached_t Figure, } /***** Trivial check *****/ - if (Figure == FigCch_UNKNOWN) - return Found; + if (Figure == FigCch_UNKNOWN || // Unknown figure + Scope == Hie_UNK) // Unknown scope + return false; /***** Get figure's value if cached and recent *****/ if (DB_QuerySELECT (&mysql_res,"can not get cached figure value", @@ -134,7 +144,7 @@ bool FigCch_GetFigureFromCache (FigCch_FigureCached_t Figure, " AND LastUpdate>FROM_UNIXTIME(UNIX_TIMESTAMP()-%lu)", Field[Type], (unsigned) Figure,Sco_GetDBStrFromScope (Scope),Cod, - FigCch_TIME_CACHE)) + TimeCached[Scope])) { /* Get row */ row = mysql_fetch_row (mysql_res); diff --git a/swad_figure_cache.h b/swad_figure_cache.h index 8ee1026d..07e7d008 100644 --- a/swad_figure_cache.h +++ b/swad_figure_cache.h @@ -62,6 +62,8 @@ typedef enum FigCch_NUM_STDS_PER_CRS = 17, // Number of students per course FigCch_NUM_NETS_PER_CRS = 18, // Number of non-editing teachers per course FigCch_NUM_TCHS_PER_CRS = 19, // Number of teachers per course + //-------------------------------------------------------------------------- + FigCch_NUM_USRS_CTY = 20, // Number of users who claim to belong to country } FigCch_FigureCached_t; #define FigCch_NUM_TYPES 2 diff --git a/swad_institution.c b/swad_institution.c index baac9d1d..47c43f73 100644 --- a/swad_institution.c +++ b/swad_institution.c @@ -33,6 +33,7 @@ #include "swad_database.h" #include "swad_department.h" #include "swad_figure.h" +#include "swad_figure_cache.h" #include "swad_form.h" #include "swad_forum.h" #include "swad_global.h" @@ -385,6 +386,7 @@ static void Ins_ListOneInstitutionForSeeing (struct Instit *Ins,unsigned NumIns) const char *TxtClassNormal; const char *TxtClassStrong; const char *BgColor; + unsigned NumUsrsInCrss; Ins_StatusTxt_t StatusTxt; if (Ins->Status & Ins_STATUS_BIT_PENDING) @@ -441,10 +443,18 @@ static void Ins_ListOneInstitutionForSeeing (struct Instit *Ins,unsigned NumIns) /* Number of users in courses of this institution */ HTM_TD_Begin ("class=\"%s RM %s\"",TxtClassNormal,BgColor); - HTM_Unsigned (Usr_GetNumUsrsInCrss (Hie_INS,Ins->InsCod, - 1 << Rol_STD | - 1 << Rol_NET | - 1 << Rol_TCH)); // Any user + if (!FigCch_GetFigureFromCache (FigCch_NUM_USRS_IN_CRSS,Hie_INS,Ins->InsCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss)) + { + // Not updated recently in cache ==> compute and update it in cache + NumUsrsInCrss = Usr_GetNumUsrsInCrss (Hie_INS,Ins->InsCod, + 1 << Rol_STD | + 1 << Rol_NET | + 1 << Rol_TCH); // Any user + FigCch_UpdateFigureIntoCache (FigCch_NUM_USRS_IN_CRSS,Hie_INS,Ins->InsCod, + FigCch_Type_UNSIGNED,&NumUsrsInCrss); + } + HTM_Unsigned (NumUsrsInCrss); HTM_TD_End (); /***** Institution status *****/