Specify the number of leader or helper sessions for each attendee. Note that this will overwrite the previous value.

"); $session_count = array(); $session_count['0'] = 0; $session_count['1'] = 1; $session_count['2'] = 2; $session_count['3'] = 3; $session_count['4'] = 4; $session_count['5'] = 5; $form['booking-leadhelp-count'] = array( '#type' => 'select', '#title' => t('Set Number of Sessions'), '#required' => TRUE, '#default_value' => '0', '#options' => $session_count, ); $header = array ( 'booking_nid' => array('data' => t('Booking ID')), 'booking_name' => array('data' => t('Name')), 'booking_gender' => array('data' => t('Gender')), 'booking_baptised' => array('data' => t('Baptised?')), 'booking_age' => array('data' => t('Age at start of event')), 'booking_lead_sessions' => array('data' => t('Total number of groups to lead')), 'booking_available_lead' => array('data' => t('Remaining slots')), 'booking_help_sessions' => array('data' => t('Total number of groups to help')), 'booking_available_help' => array('data' => t('Remaining slots')), ); $result = db_query("SELECT * FROM {booking_person} p left outer join {booking_leadhelp_list} l on p.nid = l.booking_node_id WHERE p.booking_event_id = :eid ORDER BY p.booking_gender DESC, p.booking_dob, p.booking_baptised", array(':eid' => $event->eid)); foreach($result as $data) { $options[$data->nid] = array ( 'booking_nid' => l(t('!id', array('!id' => $data->nid)), t('node/!id', array('!id' => $data->nid))), 'booking_name' => $data->booking_firstname . " " . $data->booking_lastname, 'booking_gender' => $data->booking_gender == 'M' ? 'Male' : 'Female', 'booking_baptised' => $data->booking_baptised == 'Y' ? 'Yes' : 'No', 'booking_age' => _booking_avg_age($data->booking_dob, 1, $event->booking_event_start), 'booking_lead_sessions' => $data->booking_total_lead === NULL ? '0' : $data->booking_total_lead, 'booking_available_lead' => $data->booking_available_lead === NULL ? '0' : $data->booking_available_lead, 'booking_help_sessions' => $data->booking_total_help === NULL ? '0' : $data->booking_total_help, 'booking_available_help' => $data->booking_available_help === NULL ? '0' : $data->booking_available_help, ); } $form['table'] = array ( '#type' => 'tableselect', '#header' => $header, '#options' => $options, '#empty' => t('No attendees found.'), ); $form['submit-leader'] = array ( '#type' => 'submit', '#value' => t('Set Leader'), ); $form['submit-helper'] = array ( '#type' => 'submit', '#value' => t('Set Helper'), ); return array ( 'first_para' => array ( '#type' => 'markup', '#markup' => $prefix, ), 'form' => $form, ); } /** * Process the submission for number of study group sessions to lead/help */ function booking_available_leadhelp_select_form_submit($form, &$form_state) { global $event; $counter = 0; $checkboxes = $form_state['values']['table']; $session_count = $form_state['values']['booking-leadhelp-count']; foreach($checkboxes as $key => $value) { if (is_numeric($key) && $value != 0) { //watchdog('booking', 'Checking !nid for entry in the booking_leadhelp_list table', array ('!nid' => $key)); //check if an entry already exists for this person in the booking_leadhelp_list table $person = db_query("SELECT person.booking_node_id " . "FROM {booking_leadhelp_list} person " . "WHERE booking_node_id = :nid", array(':nid' => $key)) ->fetchObject(); if ($form_state['values']['op'] == 'Set Leader') { watchdog('booking', 'Setting group leader count to !count for !nid', array ('!count' => $session_count, '!nid' => $key)); if ($person) { //run an update db_update('booking_leadhelp_list') ->fields(array ( 'booking_total_lead' => $session_count, 'booking_available_lead' => $session_count, )) ->condition('booking_node_id', $key) ->execute(); } else { //run an insert db_insert('booking_leadhelp_list') ->fields(array( 'booking_eventid' => $event->eid, 'booking_node_id' => $key, 'booking_total_lead' => $session_count, 'booking_available_lead' => $session_count, 'booking_total_help' => 0, 'booking_available_help' => 0, )) ->execute(); } } elseif ($form_state['values']['op'] == 'Set Helper') { watchdog('booking', 'Setting group helper count to !count for !nid', array ('!count' => $session_count, '!nid' => $key)); if ($person) { //run an update db_update('booking_leadhelp_list') ->fields(array ( 'booking_total_help' => $session_count, 'booking_available_help' => $session_count, )) ->condition('booking_node_id', $key) ->execute(); } else { //run an insert db_insert('booking_leadhelp_list') ->fields(array( 'booking_eventid' => $event->eid, 'booking_node_id' => $key, 'booking_total_lead' => 0, 'booking_available_lead' => 0, 'booking_total_help' => $session_count, 'booking_available_help' => $session_count, )) ->execute(); } } $counter++; } } drupal_set_message("Defined leader/helper numbers for $counter people.", 'status', FALSE); watchdog('booking', "Defined leader/helper numbers for $counter people."); } /** * Function for defining the number of study group sessions * Note: This is hard-coded for now in the install file */ /** * Function to correctly clone an associative array storing objects * Taken from http://stackoverflow.com/questions/1532618/is-there-a-function-to-make-a-copy-of-a-php-array-to-another */ function clone_array($copied_array) { return array_map(function($element) { return ( ((is_array($element)) ? call_user_func(__FUNCTION__, $element) : ((is_object($element)) ? clone $element : $element ) ) ); }, $copied_array); } //taken from http://stackoverflow.com/questions/4102777/php-random-shuffle-array-maintaining-key-value function shuffle_assoc($list) { if (!is_array($list)) return $list; $keys = array_keys($list); shuffle($keys); $random = array(); foreach ($keys as $key) { $random[$key] = $list[$key]; } return $random; } /** * Function for calculating who belongs to which study group */ function booking_studygroups_calculate() { global $event; //master attendee list $attendees = array(); //temporary working copy of attendee list $working_list = array(); //create an array to keep track of the number of people in each session for each group $session_count = array(); //delete from booking_studygroup_mapping; //alter table booking_studygroup_mapping AUTO_INCREMENT=1; //consider using a lock as per https://api.drupal.org/api/drupal/includes!lock.inc/group/lock/7 //select all the study groups for this event id $studygroups_query = db_query("SELECT * FROM {booking_studygroup_list} WHERE booking_eventid = :eid", array(':eid' => $event->eid)); $studygroups = $studygroups_query->fetchAllAssoc('sid'); //calculate the max number of attendees in a group $firstgroup = reset($studygroups); $limit = variable_get('booking_regn_limit','500'); //add an extra one to the maximum size, to cater for some larger groups when the number of people doesn't divide evenly $max_people = (int) ($limit / $firstgroup->booking_num_group_sessions) + 1; //select all the attendees booked in $query = db_query("SELECT p.nid, p.booking_partner_id, p.booking_event_id, p.booking_status, l.booking_total_lead, l.booking_available_lead, l.booking_total_help, l.booking_available_help FROM {booking_person} p left outer join {booking_leadhelp_list} l on p.nid = l.booking_node_id WHERE p.booking_event_id = :eid AND p.booking_status = 1", array(':eid' => $event->eid)); $attendees = $query->fetchAllAssoc('nid'); //select any entries already in the mapping table $group_mapping_query = db_query("SELECT * FROM {booking_studygroup_mapping} WHERE booking_eventid = :eid", array(':eid' => $event->eid)); $group_mapping = $group_mapping_query->fetchAllAssoc('sid'); //iterate over the attendee associative array and add some fields foreach ($attendees as $person) { //flag that indicates processed or not $person->processed = 0; //field that indicates the session id the person is assigned to $person->session = 0; $person->is_leader = 0; $person->is_helper = 0; //convert NULLs into zero $person->booking_total_lead = $person->booking_total_lead === NULL ? '0' : $person->booking_total_lead; $person->booking_available_lead = $person->booking_available_lead === NULL ? '0' : $person->booking_available_lead; $person->booking_total_help = $person->booking_total_help === NULL ? '0' : $person->booking_total_help; $person->booking_available_help = $person->booking_available_help === NULL ? '0' : $person->booking_available_help; } //watchdog('booking', "Attendee list: @info", array('@info' => var_export($attendees, TRUE))); //iterate over each study group (eg Monday Tuesday Wednesday etc) foreach ($studygroups as $group) { drupal_set_message(t('Processing study group !group with !sessions sessions.', array('!group' => $group->booking_studygroup_descrip, '!sessions' => $group->booking_num_group_sessions))); //create a temporary copy of the attendee list to work with for this study group $working_list = array(); $working_list = shuffle_assoc(clone_array($attendees)); //set up the iterator $obj = new ArrayObject( $working_list ); $it = $obj->getIterator(); //clear the array keeping track of the number of people in each session for this group for ($i = 1; $i <= $group->booking_num_group_sessions; $i++) $session_count[$i] = 0; //reset the iterator to starting position $it->rewind(); //watchdog('booking', "Attendee list: @info", array('@info' => var_export($attendees, TRUE))); //watchdog('booking', "Attendee list working copy: @info", array('@info' => var_export($working_list, TRUE))); $session_id = 1; $rewound = FALSE; $leader_found = FALSE; $helper_found = FALSE; while (true) { //if we're at the end of the attendee list, go back to the start if (! $it->valid() ) { if ($rewound == TRUE) { //watchdog('booking', "Already rewound once."); drupal_set_message(t("Error: Reached end of attendee list before allocating all study group leaders/helpers for group !group.", array('!group' => $group->booking_studygroup_descrip)), 'error', FALSE); //we've already gone back to the start once, don't do it again break; } else { watchdog('booking', "Rewinding to start of attendee list."); $it->rewind(); $rewound = TRUE; } } //check if we have reached the total number of sessions required if ($session_id > $group->booking_num_group_sessions) { break; } //check if we can increment the session count if ($leader_found == TRUE && $helper_found == TRUE) { $session_id++; $leader_found = FALSE; $helper_found = FALSE; } //get the current attendee element $current = $it->current(); $key = $it->key(); //watchdog('booking', 'Attendee before leader check has id !id.', array('!id' => $it->key())); //check if this attendee can be a leader if ($leader_found == FALSE && $current->processed == 0 && $current->booking_available_lead > 0) { drupal_set_message(t('Found available leader with id !id for session !session (currently with !num people).', array('!id' => $it->key(), '!session' => $session_id, '!num' => $session_count[$session_id]))); $leader_found = TRUE; //assign leader to session and mark as processed in the temporary attendee list $current->session = $session_id; $current->processed = 1; $current->is_leader = 1; //keep track of the number of people in this study group session $session_count[$session_id]++; //decrement the number of available leading positions for this user in our master copy of the attendee list $attendees[$key]->booking_available_lead = $attendees[$key]->booking_available_lead - 1; $partner_id = $current->booking_partner_id; //Check for spouse of leader, allocate to this group also if ($partner_id > 0) { //add the spouse to the same session and mark as processed in the temporary attendee list drupal_set_message(t('Assigning spouse (id !spouse) of id !id to session !session (currently with !num people).', array('!id' => $it->key(), '!session' => $session_id, '!spouse' => $partner_id, '!num' => $session_count[$session_id]))); //$spouse = ; $working_list[$partner_id]->session = $session_id; $working_list[$partner_id]->processed = 1; $session_count[$session_id]++; } } //check if this attendee can be a helper elseif ($helper_found == FALSE && $current->processed == 0 && $current->booking_available_help > 0) { drupal_set_message(t('Found available helper with id !id for session !session (currently with !num people).', array('!id' => $it->key(), '!session' => $session_id, '!num' => $session_count[$session_id]))); $helper_found = TRUE; //assign leader to session and mark as processed in the temporary attendee list $current->session = $session_id; $current->processed = 1; $current->is_helper = 1; $session_count[$session_id]++; //decrement the number of available helping positions for this user in our master copy of the attendee list $attendees[$key]->booking_available_help = $attendees[$key]->booking_available_help - 1; $partner_id = $current->booking_partner_id; //Check for spouse of helper, allocate to this group also if ($partner_id > 0) { //add the spouse to the same session and mark as processed in the temporary attendee list drupal_set_message(t('Assigning spouse (id !spouse) of id !id to session !session.', array('!id' => $it->key(), '!session' => $session_id, '!spouse' => $current->booking_partner_id))); $session_count[$session_id]++; $working_list[$partner_id]->session = $session_id; $working_list[$partner_id]->processed = 1; } } //this attendee can be neither a leader nor a helper, go to the next attendee else { $it->next(); } } //watchdog('booking', "Attendee list: @info", array('@info' => var_export($session_count, TRUE))); //reset the iterator to starting position of the attendee list $it->rewind(); //set our counter $i = 1; //iterate over the attendee list while ( $it->valid() ) { //go to the first unprocessed attendee while ($it->current()->processed == 1) { drupal_set_message(t('Person with id !id has already been processed earlier.', array('!id' => $it->key()))); $it->next(); //double check we didn't run out of people right here if (! $it->valid() ) { drupal_set_message("Ran out of attendees to process."); break; } } //cycle the session counter if we already reached the end if ($i > $group->booking_num_group_sessions) $i = 1; //check this session has room in it $break_condition = false; while ($session_count[$i] >= $max_people) { drupal_set_message(t("Session ID !id is full, moving to next session for user id !nid", array('!id' => $i, '!nid' => $it->key()))); $i++; //reset the counter if we go past the end if ($i > $group->booking_num_group_sessions) { $i = 1; //make sure we don't get stuck in an infinite loop - only go past the end once if ($break_condition == true) { drupal_set_message("Ran out of sessions that aren't full to place attendees into."); break; } $break_condition = true; } } //get the current attendee element $current = $it->current(); //assign this attendee to this session if unprocessed so far if ($current->processed == 0) { drupal_set_message(t('Assigning person with id !id to session !session (now with !num people).', array('!id' => $it->key(), '!session' => $i, '!num' => $session_count[$i]))); $current->session = $i; $current->processed = 1; $partner_id = $current->booking_partner_id; $session_count[$i]++; //check if the attendee was married if ($partner_id > 0) { //add the spouse to the same session and mark as processed in the temporary attendee list drupal_set_message(t('Assigning spouse (id !spouse) of id !id to session !session.', array('!id' => $it->key(), '!session' => $i, '!spouse' => $current->booking_partner_id))); $working_list[$partner_id]->session = $i; $working_list[$partner_id]->processed = 1; $session_count[$i]++; } } //move to the next unprocessed attendee in the list while ($it->valid() && $it->current()->processed == 1) { //drupal_set_message(t("Skipping attendee ID !id, already processed.", array('!id' => $it->key()))); $it->next(); } //move to the next session $i++; } //finished looping through attendees for this study group //iterate over the attendee list and write to the database the session they're assigned to //use the multi-insert query type at https://drupal.org/node/310079 $insert_query = db_insert('booking_studygroup_mapping')->fields(array('booking_eventid', 'booking_node_id', 'booking_studygroup_id', 'booking_session_id', 'booking_is_leader', 'booking_is_helper')); foreach($working_list as $person) { //TODO: a check to update existing records rather than inserting new one // if already in $group_mapping then just run an update query here $found = FALSE; foreach ($group_mapping as $mapping) { //check if we can find a study group session already for this user and this study group (eg Monday Tuesday or Wednesday) if ($person->nid == $mapping->booking_node_id && $group->sid == $mapping->booking_studygroup_id) { $found = TRUE; drupal_set_message(t('Found existing study group session for user id !id. Running update query on table id !sid to set session id to !session', array('!id' => $person->nid, '!sid' => $mapping->sid, '!session' => $person->session))); //update the entry db_update('booking_studygroup_mapping') ->fields(array( 'booking_session_id' => $person->session, 'booking_is_leader' => $person->is_leader, 'booking_is_helper' => $person->is_helper, )) ->condition('sid', $mapping->sid) ->execute(); break; } } //if we didn't find an existing record, add it to the list to insert if (! $found) { drupal_set_message(t('Found no existing study group session for user id !id. Adding to list of inserts.', array('!id' => $person->nid, '!sid' => $mapping->sid, '!session' => $person->session))); $record = array( 'booking_eventid' => $event->eid, 'booking_node_id' => $person->nid, 'booking_studygroup_id' => $group->sid, 'booking_session_id' => $person->session, 'booking_is_leader' => $person->is_leader, 'booking_is_helper' => $person->is_helper, ); $insert_query->values($record); } } $insert_query->execute(); //watchdog('booking', "Attendee list: @info", array('@info' => var_export($session_count, TRUE))); //clear the arrays we've been using for this iteration unset($session_count); unset($working_list); // } //finished processing study groups //watchdog('booking', "Attendee list final version: @info", array('@info' => var_export($attendees, TRUE))); } /** * Function for listing the study groups and links to view each one */ function booking_studygroups_view_summary() { global $event; $output = ""; $header = array('Link','Study Group', 'Session Count'); $attributes = array('style' => 'max-width:30%'); //get study groups $query = db_select('booking_studygroup_list', 's') ->fields('s') ->condition('s.booking_eventid', $event->eid, '='); $result = $query->execute(); foreach ($result as $group) { $rows[] = array( l(t('!id', array('!id' => $group->sid)), t('admin/config/booking/studygroups/!id/view', array('!id' => $group->sid))), $group->booking_studygroup_descrip, $group->booking_num_group_sessions, ); } //output everything $output .= t("

!event Study Groups

", array('!event' => $event->booking_eventname)); $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => $attributes)); return $output; } /** * Function for viewing who belongs to which study group */ function booking_studygroups_view_form($node, &$form_state, $group_id) { global $event; $form = array(); $options = array(); $last_session = ""; //attach the custom css $form['#attached']['css'] = array( drupal_get_path('module', 'booking') . '/booking.css', ); //verify that $group_id is a number if (! preg_match('/^[0-9]+$/', $group_id)) { drupal_set_message("Error: Invalid study group ID supplied. Unable to view group membership.", 'error', FALSE); drupal_goto('admin/config/booking'); return ""; } //collect information on the study group $group = db_query("SELECT * FROM {booking_studygroup_list} WHERE booking_eventid = :eid and sid = :sid", array(':eid' => $event->eid, ':sid' => $group_id)) ->fetchObject(); if (! $group) { drupal_set_message("Error: Could not find matching study group ID. Unable to view group membership.", 'error', FALSE); drupal_goto('admin/config/booking'); return ""; } $header = array( 'booking_session_id' => array('data' => t('Study Group Session'), 'field' => 'm.booking_session_id', 'sort' => 'asc'), 'booking_name' => array('data' => t('Name'), 'field' => 'p.booking_lastname', 'sort' => 'asc'), 'booking_is_leader' => array('data' => t('Leader?'), 'field' => 'm.booking_is_leader', 'sort' => 'asc'), 'booking_is_helper' => array('data' => t('Helper?'), 'field' => 'm.booking_is_helper', 'sort' => 'asc'), ); $query = db_select('booking_person', 'p'); $query->leftJoin('booking_studygroup_mapping', 'm', 'm.booking_node_id = p.nid'); $db_and = db_and(); $db_and->condition('p.booking_event_id', $event->eid, '='); $db_and->condition('m.booking_studygroup_id', $group_id, '='); $query->condition($db_and); $query->fields('p')->fields('m'); $table_sort = $query->extend('TableSort')->orderbyHeader($header); $result = $table_sort->execute(); //watchdog('booking', 'Study groups raw data: @info', array ('@info' => var_export($result, TRUE))); foreach($result as $data) { $class = ""; //apply theme as required if ($data->booking_is_leader == 1) $class = "leader-row"; elseif ($data->booking_is_helper == 1) $class = "helper-row"; else $class = "normal-row"; //Add a different id for first entry of new session, with a border-top to distinguish it if ($last_session <> $data->booking_session_id && $last_session <> "") { if ($class === "leader-row") $class = "leader-new-group-row"; elseif ($class === "helper-row") $class = "helper-new-group-row"; else $class = "new-group-row"; } $options[$data->nid] = array ( 'booking_session_id' => $data->booking_session_id, 'booking_name' => $data->booking_firstname . " " . $data->booking_lastname, 'booking_is_leader' => $data->booking_is_leader == 1 ? 'Yes' : 'No', 'booking_is_helper' => $data->booking_is_helper == 1 ? 'Yes' : 'No', '#attributes' => array('id' => array($class)) ); $last_session = $data->booking_session_id; } $prefix = t("

Study Group !descrip

", array('!descrip' => $group->booking_studygroup_descrip)); $form['table'] = array ( '#type' => 'tableselect', '#header' => $header, '#options' => $options, //'#attributes' => array('id' => 'sort-table'), ); return array ( 'first_para' => array ( '#type' => 'markup', '#markup' => $prefix, ), 'form' => $form, ); }