From e543b6e153cfd0fd0061da8b218de9dede072203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Massart?= Date: Mon, 10 Feb 2025 14:58:44 +0800 Subject: [PATCH] Add report builder datasource to list all session attendees --- .../reportbuilder/entities/facetoface.php | 310 ++++++++++++++++++ .../local/reportbuilder/entities/session.php | 298 +++++++++++++++++ .../reportbuilder/entities/session_date.php | 114 +++++++ .../local/reportbuilder/entities/signup.php | 105 ++++++ .../reportbuilder/entities/signup_live.php | 228 +++++++++++++ .../reportbuilder/entities/signup_status.php | 187 +++++++++++ classes/reportbuilder/datasource/signups.php | 131 ++++++++ lang/en/facetoface.php | 5 + 8 files changed, 1378 insertions(+) create mode 100644 classes/local/reportbuilder/entities/facetoface.php create mode 100644 classes/local/reportbuilder/entities/session.php create mode 100644 classes/local/reportbuilder/entities/session_date.php create mode 100644 classes/local/reportbuilder/entities/signup.php create mode 100644 classes/local/reportbuilder/entities/signup_live.php create mode 100644 classes/local/reportbuilder/entities/signup_status.php create mode 100644 classes/reportbuilder/datasource/signups.php diff --git a/classes/local/reportbuilder/entities/facetoface.php b/classes/local/reportbuilder/entities/facetoface.php new file mode 100644 index 00000000..d7d9dde5 --- /dev/null +++ b/classes/local/reportbuilder/entities/facetoface.php @@ -0,0 +1,310 @@ +. + +namespace mod_facetoface\local\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\number; +use core_reportbuilder\local\filters\select; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; +use lang_string; + +defined('MOODLE_INTERNAL') || die(); +require_once($CFG->dirroot . '/mod/facetoface/lib.php'); + +/** + * Entity. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class facetoface extends base { + + protected function get_default_entity_title(): lang_string { + return new lang_string('pluginname', 'mod_facetoface'); + } + + protected function get_default_tables(): array { + return ['facetoface']; + } + + public function initialise(): base { + + $this->initialise_columns(); + + foreach ($this->make_all_filters() as $filter) { + $this->add_filter($filter); + $this->add_condition($filter); + } + + return $this; + } + + /** + * Initialise columns. + * + * @return self + */ + protected function initialise_columns(): self { + $table = $this->get_table_alias('facetoface'); + + $column = (new column('name', new lang_string('name', 'core'), $this->get_entity_name())) + ->add_field("{$table}.name") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('shortname', new lang_string('shortname', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.shortname") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('intro', new lang_string('description', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.intro") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_LONGTEXT); + $this->add_column($column); + + $column = (new column('thirdparty', new lang_string('thirdpartyemailaddress', 'mod_facetoface'), + $this->get_entity_name())) + ->add_field("{$table}.thirdparty") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT); + $this->add_column($column); + + $column = (new column('thirdpartywaitlist', new lang_string('thirdpartywaitlist', 'mod_facetoface'), + $this->get_entity_name())) + ->add_field("{$table}.thirdpartywaitlist") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('reminderperiod', new lang_string('reminderperiod', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.reminderperiod") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('showoncalendar', new lang_string('showoncalendar', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.showoncalendar") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->add_callback(static function (?int $value) { + if ($value === F2F_CAL_NONE) { + return get_string('none', 'core'); + } else if ($value === F2F_CAL_COURSE) { + return get_string('course', 'core'); + } else if ($value === F2F_CAL_SITE) { + return get_string('site', 'core'); + } + return $value; + }); + $this->add_column($column); + + $column = (new column('approvalreqd', new lang_string('approvalreqd', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.approvalreqd") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('usercalentry', new lang_string('usercalentry', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.usercalentry") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('timecreated', new lang_string('timecreated', 'core_reportbuilder'), $this->get_entity_name())) + ->add_field("{$table}.timecreated") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('timemodified', new lang_string('timemodified', 'core_reportbuilder'), $this->get_entity_name())) + ->add_field("{$table}.timemodified") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('signuptype', new lang_string('signuptype', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.signuptype") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->add_callback(static function(?int $value) { + if ($value === MOD_FACETOFACE_SIGNUP_SINGLE) { + return get_string('single', 'mod_facetoface'); + } else if ($value === MOD_FACETOFACE_SIGNUP_MULTIPLE) { + return get_string('multiple', 'mod_facetoface'); + } + return $value; + }); + $this->add_column($column); + + $column = (new column('multiplesignupmethod', new lang_string('multiplesignupmethod', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.multiplesignupmethod") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->add_callback(static function(?int $value) { + if ($value === MOD_FACETOFACE_SIGNUP_MULTIPLE_PER_SESSION) { + return get_string('multiplesignuppersession', 'mod_facetoface'); + } else if ($value === MOD_FACETOFACE_SIGNUP_MULTIPLE_PER_ACTIVITY) { + return get_string('multiplesignupperactivity', 'mod_facetoface'); + } + return $value; + }); + $this->add_column($column); + + return $this; + } + + /** + * Make all the filters. + * + * @return filter[] + */ + protected function make_all_filters(): array { + $table = $this->get_table_alias('facetoface'); + $filters = []; + + $filters[] = (new filter( + text::class, + 'name', + new lang_string('name', 'core'), + $this->get_entity_name(), + "{$table}.name" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'shortname', + new lang_string('shortname', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.shortname" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'intro', + new lang_string('description', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.intro" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'thirdparty', + new lang_string('thirdpartyemailaddress', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.thirdparty" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'thirdpartywaitlist', + new lang_string('thirdpartywaitlist', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.thirdpartywaitlist" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + select::class, + 'showoncalendar', + new lang_string('showoncalendar', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.showoncalendar" + ))->set_options([ + F2F_CAL_NONE => get_string('none', 'core'), + F2F_CAL_COURSE => get_string('course', 'core'), + F2F_CAL_SITE => get_string('site', 'core'), + ])->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'approvalreqd', + new lang_string('approvalreqd', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.approvalreqd" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'usercalentry', + new lang_string('usercalentry', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.usercalentry" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + "{$table}.timecreated" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timemodified', + new lang_string('timemodified', 'core_reportbuilder'), + $this->get_entity_name(), + "{$table}.timemodified" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + select::class, + 'signuptype', + new lang_string('signuptype', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.signuptype" + ))->set_options([ + MOD_FACETOFACE_SIGNUP_SINGLE => get_string('single', 'mod_facetoface'), + MOD_FACETOFACE_SIGNUP_MULTIPLE => get_string('multiple', 'mod_facetoface'), + ])->add_joins($this->get_joins()); + + $filters[] = (new filter( + select::class, + 'multiplesignupmethod', + new lang_string('multiplesignupmethod', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.multiplesignupmethod" + ))->set_options([ + MOD_FACETOFACE_SIGNUP_MULTIPLE_PER_SESSION => get_string('multiplesignuppersession', 'mod_facetoface'), + MOD_FACETOFACE_SIGNUP_MULTIPLE_PER_ACTIVITY => get_string('multiplesignupperactivity', 'mod_facetoface'), + ])->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'reminderperiod', + new lang_string('reminderperiod', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.reminderperiod" + ))->add_joins($this->get_joins()); + + return $filters; + } + +} diff --git a/classes/local/reportbuilder/entities/session.php b/classes/local/reportbuilder/entities/session.php new file mode 100644 index 00000000..4c13cda1 --- /dev/null +++ b/classes/local/reportbuilder/entities/session.php @@ -0,0 +1,298 @@ +. + +namespace mod_facetoface\local\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\number; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\helpers\database; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; +use lang_string; + +/** +* Entity. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class session extends base { + + /** @var array */ + protected $customfields; + + protected function get_default_entity_title(): lang_string { + return new lang_string('sessions', 'mod_facetoface'); + } + + protected function get_default_tables(): array { + return ['facetoface_sessions', 'facetoface_session_field', 'facetoface_session_data']; + } + + public function initialise(): base { + $this->initialise_columns(); + + foreach ($this->make_all_filters() as $filter) { + $this->add_filter($filter); + $this->add_condition($filter); + } + + return $this; + } + + /** + * Initialise columns. + * + * @return self + */ + protected function initialise_columns(): self { + global $DB; + + $table = $this->get_table_alias('facetoface_sessions'); + + $column = (new column('capacity', new lang_string('capacity', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.capacity") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('allowoverbook', new lang_string('allowoverbook', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.allowoverbook") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('details', new lang_string('details', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.details") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_LONGTEXT); + $this->add_column($column); + + $column = (new column('datetimeknown', new lang_string('sessiondatetimeknown', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.datetimeknown") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('duration', new lang_string('duration', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.duration") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('normalcost', new lang_string('normalcost', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.normalcost") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('discountcost', new lang_string('discountcost', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.discountcost") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('allowcancellations', new lang_string('allowcancellations', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.allowcancellations") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('timecreated', new lang_string('timecreated', 'core_reportbuilder'), $this->get_entity_name())) + ->add_field("{$table}.timecreated") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('timemodified', new lang_string('timemodified', 'core_reportbuilder'), $this->get_entity_name())) + ->add_field("{$table}.timemodified") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true); + $this->add_column($column); + + // Adding custom fields. + foreach ($this->get_custom_fields() as $field) { + $fieldalias = $field->alias; + $column = (new column( + 'customfield_' . $field->shortname, + new lang_string('customfieldcolumn', 'core_reportbuilder', $field->name), + $this->get_entity_name() + )) + ->add_joins($this->get_joins()) + ->add_join($this->get_custom_field_join($field)) + ->add_field("{$fieldalias}.data") + ->set_type(column::TYPE_TEXT); + $this->add_column($column); + } + + return $this; + } + + /** + * Make all the filters. + * + * @return filter[] + */ + protected function make_all_filters(): array { + $table = $this->get_table_alias('facetoface_sessions'); + $filters = []; + + $filters[] = (new filter( + text::class, + 'details', + new lang_string('details', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.details" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'capacity', + new lang_string('capacity', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.capacity" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'normalcost', + new lang_string('normalcost', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.normalcost" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'duration', + new lang_string('duration', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.duration" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'discountcost', + new lang_string('discountcost', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.discountcost" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'allowoverbook', + new lang_string('allowoverbook', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.allowoverbook" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'datetimeknown', + new lang_string('sessiondatetimeknown', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.datetimeknown" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'allowcancellations', + new lang_string('allowcancellations', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.allowcancellations" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + "{$table}.timecreated" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timemodified', + new lang_string('timemodified', 'core_reportbuilder'), + $this->get_entity_name(), + "{$table}.timemodified" + ))->add_joins($this->get_joins()); + + // Adding custom fields. + foreach ($this->customfields as $field) { + $fieldalias = $field->alias; + $filter = (new filter( + text::class, + 'customfield_' . $field->shortname, + new lang_string('customfieldcolumn', 'core_reportbuilder', $field->name), + $this->get_entity_name(), + "{$fieldalias}.data" + )) + ->add_joins($this->get_joins()) + ->add_join($this->get_custom_field_join($field)); + $filters[] = $filter; + } + + return $filters; + } + + /** + * Get a customfield join. + * + * @param stdClass $field The custom field. + * @return string + */ + protected function get_custom_field_join($field): string { + $table = $this->get_table_alias('facetoface_sessions'); + + $fieldalias = $field->alias; + $join = "LEFT JOIN {facetoface_session_data} {$fieldalias} + ON {$fieldalias}.sessionid = {$table}.id + AND {$fieldalias}.fieldid = {$field->id}"; + + return $join; + } + + /** + * Get the custom fields. + * + * @return array + */ + protected function get_custom_fields(): array { + global $DB; + if (!isset($this->customfields)) { + $this->customfields = array_map(function($record) { + return (object) [ + 'id' => $record->id, + 'shortname' => $record->shortname, + 'name' => $record->name, + 'alias' => database::generate_alias(), + ]; + }, $DB->get_records('facetoface_session_field', [], 'name ASC')); + } + return $this->customfields; + } +} diff --git a/classes/local/reportbuilder/entities/session_date.php b/classes/local/reportbuilder/entities/session_date.php new file mode 100644 index 00000000..d2af12ec --- /dev/null +++ b/classes/local/reportbuilder/entities/session_date.php @@ -0,0 +1,114 @@ +. + +namespace mod_facetoface\local\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; +use lang_string; + +/** + * Entity. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class session_date extends base { + + protected function get_default_entity_title(): lang_string { + return new lang_string('sessiondates', 'mod_facetoface'); + } + + protected function get_default_tables(): array { + return ['facetoface_sessions_dates']; + } + + public function initialise(): base { + + $this->initialise_columns(); + + foreach ($this->make_all_filters() as $filter) { + $this->add_filter($filter); + $this->add_condition($filter); + } + + return $this; + } + + /** + * Initialise columns. + * + * @return self + */ + protected function initialise_columns(): self { + $table = $this->get_table_alias('facetoface_sessions_dates'); + + $column = (new column('timestart', new lang_string('timestart', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.timestart") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true) + ->add_callback(function($value) { + return userdate($value); + }); + $this->add_column($column); + + $column = (new column('timefinish', new lang_string('timefinish', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.timefinish") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true) + ->add_callback(function($value) { + return userdate($value); + }); + $this->add_column($column); + + return $this; + } + + /** + * Make all the filters. + * + * @return filter[] + */ + protected function make_all_filters(): array { + $table = $this->get_table_alias('facetoface_sessions_dates'); + $filters = []; + + $filters[] = (new filter( + date::class, + 'timestart', + new lang_string('timestart', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.timestart" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timefinish', + new lang_string('timefinish', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.timefinish" + ))->add_joins($this->get_joins()); + + return $filters; + } + +} diff --git a/classes/local/reportbuilder/entities/signup.php b/classes/local/reportbuilder/entities/signup.php new file mode 100644 index 00000000..6becd0ab --- /dev/null +++ b/classes/local/reportbuilder/entities/signup.php @@ -0,0 +1,105 @@ +. + +namespace mod_facetoface\local\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; +use lang_string; + +/** + * Entity. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class signup extends base { + + protected function get_default_entity_title(): lang_string { + return new lang_string('signups', 'mod_facetoface'); + } + + protected function get_default_tables(): array { + return ['facetoface_signups']; + } + + public function initialise(): base { + $this->initialise_columns(); + + foreach ($this->make_all_filters() as $filter) { + $this->add_filter($filter); + $this->add_condition($filter); + } + + return $this; + } + + /** + * Initialise columns. + * + * @return self + */ + protected function initialise_columns(): self { + $table = $this->get_table_alias('facetoface_signups'); + + $column = (new column('mailedreminder', new lang_string('mailedreminder', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.mailedreminder") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('discountcode', new lang_string('discountcode', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.discountcode") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT); + $this->add_column($column); + + return $this; + } + + /** + * Make all the filters. + * + * @return filter[] + */ + protected function make_all_filters(): array { + $table = $this->get_table_alias('facetoface_signups'); + $filters = []; + + $filters[] = (new filter( + boolean_select::class, + 'mailedreminder', + new lang_string('mailedreminder', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.mailedreminder" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'discountcode', + new lang_string('discountcode', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.discountcode" + ))->add_joins($this->get_joins()); + + return $filters; + } +} diff --git a/classes/local/reportbuilder/entities/signup_live.php b/classes/local/reportbuilder/entities/signup_live.php new file mode 100644 index 00000000..9602c230 --- /dev/null +++ b/classes/local/reportbuilder/entities/signup_live.php @@ -0,0 +1,228 @@ +. + +namespace mod_facetoface\local\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\autocomplete; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\number; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; +use lang_string; + +defined('MOODLE_INTERNAL') || die(); +require_once($CFG->dirroot . '/mod/facetoface/lib.php'); + +/** + * Signup live. + * + * This is the combination of signups and signups_status except that it only + * reads the latest status for each signup. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class signup_live extends base { + + protected function get_default_entity_title(): lang_string { + return new lang_string('signup', 'mod_facetoface'); + } + + protected function get_default_tables(): array { + return ['facetoface_signups', 'facetoface_signups_status']; + } + + public function initialise(): base { + + $signupsalias = $this->get_table_alias('facetoface_signups'); + $statusalias = $this->get_table_alias('facetoface_signups_status'); + + $this->add_join("LEFT JOIN {facetoface_signups_status} {$statusalias} + ON {$statusalias}.signupid = {$signupsalias}.id + AND {$statusalias}.superceded = 0"); + + $this->initialise_columns(); + + foreach ($this->make_all_filters() as $filter) { + $this->add_filter($filter); + $this->add_condition($filter); + } + + return $this; + } + + /** + * Initialise columns. + * + * @return self + */ + protected function initialise_columns(): self { + $signupstable = $this->get_table_alias('facetoface_signups'); + $statustable = $this->get_table_alias('facetoface_signups_status'); + + $column = (new column('mailedreminder', new lang_string('mailedreminder', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$signupstable}.mailedreminder") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('discountcode', new lang_string('discountcode', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$signupstable}.discountcode") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT); + $this->add_column($column); + + $column = (new column('status', new lang_string('status', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$statustable}.statuscode") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->add_callback(static function(?int $value) { + if ($value === MDL_F2F_STATUS_BOOKED) { + return get_string('status_booked', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_WAITLISTED) { + return get_string('status_waitlisted', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_REQUESTED) { + return get_string('status_requested', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_APPROVED) { + return get_string('status_approved', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_DECLINED) { + return get_string('status_declined', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_SESSION_CANCELLED) { + return get_string('status_session_cancelled', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_NO_SHOW) { + return get_string('status_no_show', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_PARTIALLY_ATTENDED) { + return get_string('status_partially_attended', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_FULLY_ATTENDED) { + return get_string('status_fully_attended', 'mod_facetoface'); + } + return $value; + }); + $this->add_column($column); + + $column = (new column('superceded', new lang_string('superceded', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$statustable}.superceded") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('grade', new lang_string('gradenoun', 'core'), $this->get_entity_name())) + ->add_field("{$statustable}.grade") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_FLOAT) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('note', new lang_string('note', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$statustable}.note") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT); + $this->add_column($column); + + $column = (new column('timecreated', new lang_string('timecreated', 'core_reportbuilder'), $this->get_entity_name())) + ->add_field("{$statustable}.timecreated") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true); + $this->add_column($column); + + return $this; + } + + /** + * Make all the filters. + * + * @return filter[] + */ + protected function make_all_filters(): array { + $signupstable = $this->get_table_alias('facetoface_signups'); + $statustable = $this->get_table_alias('facetoface_signups_status'); + $filters = []; + + $filters[] = (new filter( + boolean_select::class, + 'mailedreminder', + new lang_string('mailedreminder', 'mod_facetoface'), + $this->get_entity_name(), + "{$signupstable}.mailedreminder" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'discountcode', + new lang_string('discountcode', 'mod_facetoface'), + $this->get_entity_name(), + "{$signupstable}.discountcode" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + autocomplete::class, + 'status', + new lang_string('status', 'mod_facetoface'), + $this->get_entity_name(), + "{$statustable}.statuscode" + ))->set_options([ + MDL_F2F_STATUS_BOOKED => get_string('status_booked', 'mod_facetoface'), + MDL_F2F_STATUS_WAITLISTED => get_string('status_waitlisted', 'mod_facetoface'), + MDL_F2F_STATUS_REQUESTED => get_string('status_requested', 'mod_facetoface'), + MDL_F2F_STATUS_APPROVED => get_string('status_approved', 'mod_facetoface'), + MDL_F2F_STATUS_DECLINED => get_string('status_declined', 'mod_facetoface'), + MDL_F2F_STATUS_SESSION_CANCELLED => get_string('status_session_cancelled', 'mod_facetoface'), + MDL_F2F_STATUS_NO_SHOW => get_string('status_no_show', 'mod_facetoface'), + MDL_F2F_STATUS_PARTIALLY_ATTENDED => get_string('status_partially_attended', 'mod_facetoface'), + MDL_F2F_STATUS_FULLY_ATTENDED => get_string('status_fully_attended', 'mod_facetoface'), + ])->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'superceded', + new lang_string('superceded', 'mod_facetoface'), + $this->get_entity_name(), + "{$statustable}.superceded" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'grade', + new lang_string('gradenoun', 'core'), + $this->get_entity_name(), + "{$statustable}.grade" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'note', + new lang_string('note', 'mod_facetoface'), + $this->get_entity_name(), + "{$statustable}.note" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + "{$statustable}.timecreated" + ))->add_joins($this->get_joins()); + + return $filters; + } +} diff --git a/classes/local/reportbuilder/entities/signup_status.php b/classes/local/reportbuilder/entities/signup_status.php new file mode 100644 index 00000000..3daf70fb --- /dev/null +++ b/classes/local/reportbuilder/entities/signup_status.php @@ -0,0 +1,187 @@ +. + +namespace mod_facetoface\local\reportbuilder\entities; + +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\number; +use core_reportbuilder\local\filters\select; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; +use lang_string; + +defined('MOODLE_INTERNAL') || die(); +require_once($CFG->dirroot . '/mod/facetoface/lib.php'); + +/** + * Entity. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class signup_status extends base { + + protected function get_default_entity_title(): lang_string { + return new lang_string('signupstatus', 'mod_facetoface'); + } + + protected function get_default_tables(): array { + return ['facetoface_signups_status']; + } + + public function initialise(): base { + $this->initialise_columns(); + + foreach ($this->make_all_filters() as $filter) { + $this->add_filter($filter); + $this->add_condition($filter); + } + + return $this; + } + + /** + * Initialise columns. + * + * @return self + */ + protected function initialise_columns(): self { + $table = $this->get_table_alias('facetoface_signups_status'); + + $column = (new column('statuscode', new lang_string('status', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.statuscode") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_INTEGER) + ->add_callback(static function(?int $value) { + if ($value === MDL_F2F_STATUS_BOOKED) { + return get_string('status_booked', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_WAITLISTED) { + return get_string('status_waitlisted', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_REQUESTED) { + return get_string('status_requested', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_APPROVED) { + return get_string('status_approved', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_DECLINED) { + return get_string('status_declined', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_SESSION_CANCELLED) { + return get_string('status_session_cancelled', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_NO_SHOW) { + return get_string('status_no_show', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_PARTIALLY_ATTENDED) { + return get_string('status_partially_attended', 'mod_facetoface'); + } else if ($value === MDL_F2F_STATUS_FULLY_ATTENDED) { + return get_string('status_fully_attended', 'mod_facetoface'); + } + return $value; + }); + $this->add_column($column); + + $column = (new column('superceded', new lang_string('superceded', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.superceded") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN); + $this->add_column($column); + + $column = (new column('grade', new lang_string('gradenoun', 'core'), $this->get_entity_name())) + ->add_field("{$table}.grade") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_FLOAT) + ->set_is_sortable(true); + $this->add_column($column); + + $column = (new column('note', new lang_string('note', 'mod_facetoface'), $this->get_entity_name())) + ->add_field("{$table}.note") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TEXT); + $this->add_column($column); + + $column = (new column('timecreated', new lang_string('timecreated', 'core_reportbuilder'), $this->get_entity_name())) + ->add_field("{$table}.timecreated") + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->set_is_sortable(true); + $this->add_column($column); + + return $this; + } + + /** + * Make all the filters. + * + * @return filter[] + */ + protected function make_all_filters(): array { + $table = $this->get_table_alias('facetoface_signups_status'); + $filters = []; + + $filters[] = (new filter( + select::class, + 'statuscode', + new lang_string('status', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.statuscode" + ))->set_options([ + MDL_F2F_STATUS_BOOKED => get_string('status_booked', 'mod_facetoface'), + MDL_F2F_STATUS_WAITLISTED => get_string('status_waitlisted', 'mod_facetoface'), + MDL_F2F_STATUS_REQUESTED => get_string('status_requested', 'mod_facetoface'), + MDL_F2F_STATUS_APPROVED => get_string('status_approved', 'mod_facetoface'), + MDL_F2F_STATUS_DECLINED => get_string('status_declined', 'mod_facetoface'), + MDL_F2F_STATUS_SESSION_CANCELLED => get_string('status_session_cancelled', 'mod_facetoface'), + MDL_F2F_STATUS_NO_SHOW => get_string('status_no_show', 'mod_facetoface'), + MDL_F2F_STATUS_PARTIALLY_ATTENDED => get_string('status_partially_attended', 'mod_facetoface'), + MDL_F2F_STATUS_FULLY_ATTENDED => get_string('status_fully_attended', 'mod_facetoface'), + ])->add_joins($this->get_joins()); + + $filters[] = (new filter( + boolean_select::class, + 'superceded', + new lang_string('superceded', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.superceded" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + number::class, + 'grade', + new lang_string('gradenoun', 'core'), + $this->get_entity_name(), + "{$table}.grade" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + text::class, + 'note', + new lang_string('note', 'mod_facetoface'), + $this->get_entity_name(), + "{$table}.note" + ))->add_joins($this->get_joins()); + + $filters[] = (new filter( + date::class, + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + "{$table}.timecreated" + ))->add_joins($this->get_joins()); + + return $filters; + } +} diff --git a/classes/reportbuilder/datasource/signups.php b/classes/reportbuilder/datasource/signups.php new file mode 100644 index 00000000..089f4dc0 --- /dev/null +++ b/classes/reportbuilder/datasource/signups.php @@ -0,0 +1,131 @@ +. + +namespace mod_facetoface\reportbuilder\datasource; + +use core_reportbuilder\datasource; +use core_reportbuilder\local\entities\course; +use core_reportbuilder\local\entities\user; +use mod_facetoface\local\reportbuilder\entities\facetoface as facetoface_entity; +use mod_facetoface\local\reportbuilder\entities\session; +use mod_facetoface\local\reportbuilder\entities\session_date; +use mod_facetoface\local\reportbuilder\entities\signup_live; + +/** + * Signups. + * + * @package mod_facetoface + * @copyright 2025 Murdoch University + * @author Frédéric Massart + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class signups extends datasource { + + /** + * Return user friendly name of the datasource + * + * @return string + */ + public static function get_name(): string { + return get_string('signups', 'mod_facetoface'); + } + + /** + * Initialise report + */ + protected function initialise(): void { + $courseentity = new course(); + $facetofaceentity = new facetoface_entity(); + $sessionentity = new session(); + $sessiondateentity = new session_date(); + $signupentity = (new signup_live())->set_entity_name('signup'); + $userattendeeentity = (new user()) + ->set_entity_title(new \lang_string('attendee', 'mod_facetoface')); + + $coursealias = $courseentity->get_table_alias('course'); + $facetofacealias = $facetofaceentity->get_table_alias('facetoface'); + $sessionalias = $sessionentity->get_table_alias('facetoface_sessions'); + $sessiondatealias = $sessiondateentity->get_table_alias('facetoface_sessions_dates'); + $signupalias = $signupentity->get_table_alias('facetoface_signups'); + $userattendeealias = $userattendeeentity->get_table_alias('user'); + + $this->set_main_table('facetoface', $facetofacealias); + + $this->add_entity($courseentity); + $this->add_entity($facetofaceentity); + $this->add_entity($sessionentity); + $this->add_entity($sessiondateentity); + $this->add_entity($signupentity); + $this->add_entity($userattendeeentity); + + // Join the tables together. + $this->add_join("JOIN {course} {$coursealias} + ON {$facetofacealias}.course = {$coursealias}.id"); + $this->add_join("LEFT JOIN {facetoface_sessions} {$sessionalias} + ON {$sessionalias}.facetoface = {$facetofacealias}.id"); + $this->add_join("LEFT JOIN {facetoface_sessions_dates} {$sessiondatealias} + ON {$sessiondatealias}.sessionid = {$sessionalias}.id"); + $this->add_join("LEFT JOIN {facetoface_signups} {$signupalias} + ON {$signupalias}.sessionid = {$sessionalias}.id"); + foreach ($signupentity->get_joins() as $join) { + $this->add_join($join); + } + $this->add_join("LEFT JOIN {user} {$userattendeealias} + ON {$userattendeealias}.id = {$signupalias}.userid"); + + // Add all columns from each entity. + $this->add_all_from_entity($courseentity->get_entity_name()); + $this->add_all_from_entity($facetofaceentity->get_entity_name()); + $this->add_all_from_entity($sessionentity->get_entity_name()); + $this->add_all_from_entity($sessiondateentity->get_entity_name()); + $this->add_all_from_entity($signupentity->get_entity_name()); + $this->add_all_from_entity($userattendeeentity->get_entity_name()); + } + + /** + * Return the default columns. + * + * @return string[] + */ + public function get_default_columns(): array { + return [ + 'facetoface:name', + 'session:capacity', + 'session_date:timestart', + 'session_date:timefinish', + 'user:fullname', + 'signup:status' + ]; + } + + /** + * Return the default filters. + * + * @return string[] + */ + public function get_default_filters(): array { + return []; + } + + /** + * Return the default conditions. + * + * @return string[] + */ + public function get_default_conditions(): array { + return []; + } +} diff --git a/lang/en/facetoface.php b/lang/en/facetoface.php index c8891eb0..eaaab96d 100644 --- a/lang/en/facetoface.php +++ b/lang/en/facetoface.php @@ -54,8 +54,10 @@ $string['attendance'] = 'Attendance'; $string['attendanceinstructions'] = 'Select users who attended the session:'; $string['attendedsession'] = 'Attended session'; +$string['attendee'] = 'Attendee'; $string['attendees'] = 'Attendees'; $string['attendeesexporttofileheading'] = 'Attendees export to file'; +$string['mailedreminder'] = 'Reminder sent'; $string['potentialattendees'] = 'Potential Attendees'; $string['booked'] = 'Booked'; $string['bookingcancelled'] = 'Your booking has been cancelled.'; @@ -377,6 +379,7 @@ $string['sentremindermanager'] = 'Sent reminder email to user manager'; $string['sentreminderuser'] = 'Sent reminder email to user'; $string['sessiondate'] = 'Session date'; +$string['sessiondates'] = 'Session dates'; $string['sessiondatetime'] = 'Session date/time'; $string['sessiondatetimeknown'] = 'Session date/time known'; $string['sessionfinishtime'] = 'Session finish time'; @@ -581,6 +584,7 @@ $string['showbylocation'] = 'Show by location'; $string['showoncalendar'] = 'Calendar display settings'; $string['signup'] = 'Sign-up'; +$string['signupstatus'] = 'Signup status'; $string['single'] = 'Single'; $string['signups'] = 'Sign-ups'; $string['signupfor'] = 'Sign-up for {$a}'; @@ -596,6 +600,7 @@ $string['submissions'] = 'Submissions'; $string['submitted'] = 'Submitted'; $string['submit'] = 'Submit'; +$string['superceded'] = 'Superceded'; $string['suppressemail'] = 'Suppress email notification'; $string['status'] = 'Status'; $string['status_booked'] = 'Booked';