public function computeSample()
 {
     // Initialize the projects array.
     $this->projectIssueMetricInitProjects(array('response_rate' => 0, 'response_time' => 0));
     // Query for issues created in the previosu time period, so there is a full time period for responses.
     $query = db_select('node', 'n', array('target' => 'slave'))->condition('n.created', $this->currentSample->sample_startstamp - ($this->currentSample->sample_endstamp - $this->currentSample->sample_startstamp), '>=')->condition('n.created', $this->currentSample->sample_startstamp, '<')->condition('n.type', project_issue_issue_node_types())->condition('n.status', NODE_PUBLISHED)->groupBy('n.nid');
     $query->innerJoin('field_data_field_project', 'p', "n.nid = p.entity_id AND p.entity_type = 'node'");
     $query->addField('p', 'field_project_target_id');
     // Restrict to only the passed projects.
     if (!empty($this->currentSample->options['object_ids'])) {
         $query->condition('p.field_project_target_id', $this->currentSample->options['object_ids']);
     }
     // Issues not posted by the maintainer themself.
     $maintainers = db_select('project_maintainer', 'pm')->where('pm.nid = p.field_project_target_id')->fields('pm', array('uid'));
     $query->condition('n.uid', $maintainers, 'NOT IN');
     $query->leftJoin('comment', 'c', 'c.nid = n.nid AND c.status = :status AND c.uid <> n.uid AND c.uid <> :robot AND c.created < :end', array(':status' => COMMENT_PUBLISHED, ':robot' => variable_get('project_issue_followup_user', 0), ':end' => $this->currentSample->sample_endstamp));
     $query->addExpression('c.cid IS NOT NULL', 'responded');
     $query->addExpression('min(c.created) - n.created', 'time');
     $projects = array();
     $issues = $query->execute();
     foreach ($issues as $issue) {
         if (!isset($projects[$issue->field_project_target_id])) {
             $projects[$issue->field_project_target_id] = array('count' => 0, 'responded' => 0, 'time' => 0);
         }
         $projects[$issue->field_project_target_id]['count'] += 1;
         $projects[$issue->field_project_target_id]['responded'] += $issue->responded;
         $projects[$issue->field_project_target_id]['time'] += $issue->time;
     }
     foreach ($projects as $id => $project) {
         $this->currentSample->values[$id]['response_rate'] = round($project['responded'] / $project['count'] * 100);
         $this->currentSample->values[$id]['response_time'] = round($project['time'] / $project['count']);
     }
 }
 public function computeSample()
 {
     // Load options.
     $options = $this->currentSample->options;
     // Initialize the projects array.
     $data_types = $this->newProjectOpenedVsClosed();
     $this->projectIssueMetricInitProjects($data_types);
     // The initial arguments are the 'open' project issue status options.
     $open_states = project_issue_open_states();
     // @todo Until http://drupal.org/node/214347 is resolved, we don't want
     // the 'fixed' status in the list of open statuses, so unset it here.
     $open_states = array_diff($open_states, array(PROJECT_ISSUE_STATE_FIXED));
     // Pull last possible issue nid for the end of the period being measured.
     $max_nid = db_query('SELECT MAX(nid) FROM {node} WHERE created <= :created', array(':created' => $this->currentSample->sample_endstamp), array('target' => 'slave'))->fetchField();
     if (!empty($max_nid)) {
         // Subquery to calculate the timestamp of the applicable revision
         $tsquery = db_select('node_revision', 'sv', array('target' => 'slave'));
         $tsquery->condition('sv.timestamp', $this->currentSample->sample_endstamp, '<=');
         $tsquery->addField('sv', 'nid', 'nid');
         $tsquery->addExpression('MAX(sv.vid)', 'vid');
         $tsquery->groupBy('sv.nid');
         $tsquery->orderBy('NULL');
         $query = db_select($tsquery, 'r', array('target' => 'slave'));
         $query->innerJoin('field_revision_field_project', 'p', "r.nid = p.entity_id AND r.vid = p.revision_id AND p.entity_type = 'node'");
         $query->innerJoin('field_revision_field_issue_status', 's', "r.nid = s.entity_id AND r.vid = s.revision_id AND s.entity_type = 'node'");
         $query->innerJoin('field_revision_field_issue_category', 'c', "r.nid = c.entity_id AND r.vid = c.revision_id AND c.entity_type = 'node'");
         $query->innerJoin('node', 'n', 'n.nid = r.nid');
         $query->condition('n.type', project_issue_issue_node_types());
         $query->condition('n.nid', $max_nid, '<=');
         $query->condition('n.status', NODE_PUBLISHED);
         $query->addField('p', 'field_project_target_id', 'pid');
         $query->addField('s', 'field_issue_status_value', 'sid');
         $query->addField('c', 'field_issue_category_value', 'category');
         // Restrict to only the passed projects.
         if (!empty($options['object_ids'])) {
             $query->condition('p.field_project_target_id', $options['object_ids']);
         }
         $issues = $query->execute();
         // @todo When http://drupal.org/node/115553 lands, this hard-coded list of
         // categories will need to be handled differently.
         $categories = array(1 => 'bug', 3 => 'feature', 2 => 'task', 4 => 'support');
         foreach ($issues as $issue) {
             // Add to the total count for the category in the values array.
             if (isset($categories[$issue->category])) {
                 if (!isset($this->currentSample->values[$issue->pid])) {
                     $this->currentSample->values[$issue->pid] = array('bug_open' => 0, 'bug_closed' => 0, 'feature_open' => 0, 'feature_closed' => 0, 'task_open' => 0, 'task_closed' => 0, 'support_open' => 0, 'support_closed' => 0);
                 }
                 $this->currentSample->values[$issue->pid][$categories[$issue->category] . '_' . (in_array($issue->sid, $open_states) ? 'open' : 'closed')] += 1;
             }
         }
     }
     // Add in total counts across all categories.
     foreach ($this->currentSample->values as $project_id => $values_array) {
         $this->currentSample->values[$project_id]['total_open'] = $values_array['bug_open'] + $values_array['feature_open'] + $values_array['task_open'] + $values_array['support_open'];
         $this->currentSample->values[$project_id]['total_closed'] = $values_array['bug_closed'] + $values_array['feature_closed'] + $values_array['task_closed'] + $values_array['support_closed'];
     }
 }
 public function computeSample()
 {
     // Initialize the projects array.
     $this->projectIssueMetricInitProjects(array('reporters' => 0, 'participants' => 0));
     // Load options.
     $sample = $this->currentSample;
     $query = db_select('node', 'n', array('target' => 'slave'))->condition('n.created', $sample->sample_startstamp, '>=')->condition('n.created', $sample->sample_endstamp, '<')->condition('n.type', project_issue_issue_node_types());
     $query->innerJoin('field_data_field_project', 'p', "n.nid = p.entity_id AND p.entity_type = 'node'");
     $query->addField('p', 'field_project_target_id', 'pid');
     $query->addField('n', 'uid', 'uid');
     $query->distinct();
     // Restrict to only the passed projects.
     if (!empty($sample->options['object_ids'])) {
         $query->condition('p.field_project_target_id', $sample->options['object_ids']);
     }
     // Pull the count of unique reporters per project.
     $projects = $query->execute();
     $project_participants = array();
     foreach ($projects as $project) {
         // Increment the number of reporters for the project, and also store
         // them as a participant.
         if (isset($this->currentSample->values[$project->pid]['reporters'])) {
             $this->currentSample->values[$project->pid]['reporters'] += 1;
         } else {
             $this->currentSample->values[$project->pid]['reporters'] = 1;
         }
         $this->currentSample->values[$project->pid]['participants'] = 0;
         $project_participants[$project->pid][$project->uid] = TRUE;
     }
     // Pull the count of unique participants per project.
     $query = db_select('comment', 'c', array('target' => 'slave'))->condition('c.created', $sample->sample_startstamp, '>=')->condition('c.created', $sample->sample_endstamp, '<');
     $query->innerJoin('field_data_field_project', 'p', "c.nid = p.entity_id AND p.entity_type = 'node'");
     $query->addField('p', 'field_project_target_id', 'pid');
     $query->addField('c', 'uid');
     $query->distinct();
     $projects = $query->execute();
     // Add in participants from comments.  This will overwrite the reporters if
     // they are the same uid, thus avoiding double counting.
     foreach ($projects as $project) {
         $project_participants[$project->pid][$project->uid] = TRUE;
     }
     // Store the total participants for each project.
     foreach ($project_participants as $pid => $participants) {
         if (!isset($this->currentSample->values[$pid]['reporters'])) {
             $this->currentSample->values[$pid]['reporters'] = 0;
         }
         $this->currentSample->values[$pid]['participants'] = count($participants);
     }
     unset($project_participants);
 }
 public function computeSample()
 {
     // Initialize the projects array.
     $this->projectIssueMetricInitProjects(array('new_issues' => 0, 'new_comments' => 0, 'new_total' => 0));
     // Load options.
     $sample = $this->currentSample;
     $query = db_select('node', 'n', array('target' => 'slave'))->condition('n.created', $sample->sample_startstamp, '>=')->condition('n.created', $sample->sample_endstamp, '<')->condition('n.type', project_issue_issue_node_types());
     $query->innerJoin('field_data_field_project', 'p', "n.nid = p.entity_id AND p.entity_type = 'node'");
     $query->addField('p', 'field_project_target_id', 'pid');
     $query->addExpression('COUNT(n.nid)', 'count');
     $query->groupBy('p.field_project_target_id');
     // Restrict to only the passed projects.
     if (!empty($sample->options['object_ids'])) {
         $query->condition('p.field_project_target_id', $sample->options['object_ids']);
     }
     // Pull the count of new issues per project.
     $projects = $query->execute();
     foreach ($projects as $project) {
         $this->currentSample->values[$project->pid]['new_issues'] = (int) $project->count;
         $this->currentSample->values[$project->pid]['new_comments'] = 0;
         $this->currentSample->values[$project->pid]['new_total'] = (int) $project->count;
     }
     // Pull the count of new issue comments per project.
     $query = db_select('comment', 'c', array('target' => 'slave'))->condition('c.created', $sample->sample_startstamp, '>=')->condition('c.created', $sample->sample_endstamp, '<');
     $query->innerJoin('field_data_field_project', 'p', "c.nid = p.entity_id AND p.entity_type = 'node'");
     $query->addField('p', 'field_project_target_id', 'pid');
     $query->addExpression('COUNT(c.cid)', 'count');
     $query->groupBy('p.field_project_target_id');
     // Restrict to only the passed projects.
     if (!empty($sample->options['object_ids'])) {
         $query->condition('p.field_project_target_id', $sample->options['object_ids']);
     }
     $projects = $query->execute();
     foreach ($projects as $project) {
         if (!isset($this->currentSample->values[$project->pid]['new_issues'])) {
             $this->currentSample->values[$project->pid]['new_issues'] = 0;
         }
         $this->currentSample->values[$project->pid]['new_comments'] = (int) $project->count;
         if (isset($this->currentSample->values[$project->pid]['new_total'])) {
             $this->currentSample->values[$project->pid]['new_total'] += (int) $project->count;
         } else {
             $this->currentSample->values[$project->pid]['new_total'] = (int) $project->count;
         }
     }
 }
 /**
  * Implements EntityReferenceHandler::getReferencableEntities().
  */
 public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 25)
 {
     $options = array();
     $target_node_types = $this->field['settings']['handler_settings']['target_bundles'];
     // No target node types means all issue types may be selected.
     if (empty($target_node_types)) {
         $target_node_types = project_issue_issue_node_types();
     }
     global $base_url;
     // Early return if the short match string would generate too many results.
     // @todo: Make this configurable? Return even if user has ebereted #nid.
     // It's unlikely they're trying to reference a two digit nid issue.
     if (strlen($match) < 4) {
         return $options;
     }
     // If the given string begins with the site domain, try to match it to the
     // URL of an issue node.
     if (substr($match, 0, strlen($base_url)) == $base_url) {
         $matches = array();
         // Extract the node ID from the URL, allowing for an anchor tag.
         preg_match("@^{$base_url}/node/(\\d+)(?:#\\S+)?\$@", $match, $matches);
         if (isset($matches[1])) {
             $nid = $matches[1];
             $node = node_load($nid);
             if ($node) {
                 // Only allow the node if it's of the right type and the user has
                 // access to view it.
                 if (in_array($node->type, $target_node_types) && node_access('view', $node)) {
                     $options[$node->type][$nid] = check_plain($this->getLabel($node));
                     // Don't return yet, as there is a slim chance that the URL is part
                     // of the title of an issue which starts with the domain name or
                     // even the full URL, as in 'http://example.com/node/1 is broken'.
                 }
             }
         }
     }
     // If the given string is of the form '#1234' then try to match that as a
     // nid.
     if (strpos($match, '#') === 0) {
         if (preg_match("@^#(\\d+)\$@", $match)) {
             $nid = substr($match, 1);
             $node = node_load($nid);
             if ($node) {
                 // Only allow the node if it's of the right type and the user has
                 // access to view it.
                 if (in_array($node->type, $target_node_types) && node_access('view', $node)) {
                     $options[$node->type][$nid] = check_plain($this->getLabel($node));
                     // Don't return, same reason as above.
                 }
             }
         }
     }
     // Build a query for the nodes. We can't use buildEntityFieldQuery() because
     // we have to use a SelectQuery rather than an EntityFieldQuery to have an
     // OR condition.
     $query = db_select('node', 'n');
     if (isset($match)) {
         // Try to match on the title or nid.
         $query->condition(db_or()->condition('n.title', '%' . db_like($match) . '%', 'LIKE')->condition('n.nid', $match));
     }
     // Set the node type.
     $query->condition('type', $target_node_types);
     if (!user_access('bypass node access')) {
         // Restrict the query to published nodes.
         $query->condition('n.status', NODE_PUBLISHED);
     }
     // Restrict the number of returned rows. jQuery UI autocomplete defaults to
     // showing 10 only.
     if (!empty($limit)) {
         $query->range(0, $limit);
     }
     // Order the returned nodes by some sort of relevancy.
     $query->orderBy('n.changed', 'DESC');
     $node_data = $query->fields('n', array('nid', 'title', 'type'))->addTag('node_access')->execute()->fetchAll();
     foreach ($node_data as $item) {
         $options[$item->type][$item->nid] = check_plain($item->title);
     }
     return $options;
 }
 /**
  * Builds the historical values for total open/closed issues per project.
  *
  * @param $open_states
  *   An array of state IDs that are considered 'open'.
  * @param $where
  *   Additional filters for the query.
  * @param $args
  *   Query arguments.
  */
 protected function buildSampleResultsHistorical($open_states, $ids)
 {
     // Load current sample.
     $sample = $this->currentSample;
     $endstamp = $sample->sample_endstamp;
     // Pull last possible issue nid for the end of the period being measured.
     $max_nid = db_query('SELECT MAX(nid) FROM {node} WHERE created <= :created', array(':created' => $endstamp))->fetchField();
     if (!empty($max_nid)) {
         // Subquery to calculate the timestamp of the applicable revision
         $tsquery = db_select('node_revision', 'sv');
         $tsquery->condition('sv.timestamp', $endstamp, '<=');
         $tsquery->addField('sv', 'nid', 'nid');
         $tsquery->addExpression('MAX(sv.timestamp)', 'timestamp');
         $query = db_select($tsquery, 'sq');
         $query->innerJoin('node_revision', 'r', 'sq.nid = r.nid AND sq.timestamp = r.timestamp');
         $query->innerJoin('field_revision_field_project', 'p', "r.nid = p.entity_id AND r.vid = p.revision_id AND p.entity_type = 'node'");
         $query->innerJoin('field_revision_field_issue_status', 's', "r.nid = s.entity_id AND r.vid = s.revision_id AND s.entity_type = 'node'");
         $query->innerJoin('field_revision_field_issue_category', 'c', "r.nid = c.entity_id AND r.vid = c.revision_id AND c.entity_type = 'node'");
         $query->innerJoin('node', 'n', 'n.nid = r.nid');
         $query->condition('n.type', project_issue_issue_node_types());
         $query->condition('n.nid', $max_nid, '<=');
         $query->groupBy('n.nid');
         $query->groupBy('p.field_project_target_id');
         $query->addField('n', 'nid', 'nid');
         $query->addField('p', 'field_project_target_id', 'pid');
         $query->addField('s', 'field_issue_status_value', 'sid');
         $query->addField('c', 'field_issue_category_value', 'category');
         // Restrict to only the passed projects.
         if (!empty($ids)) {
             $query->condition('p.field_project_target_id', $ids);
         }
         $issues = $query->execute();
         // @todo When http://drupal.org/node/115553 lands, this hard-coded list of
         // categories will need to be handled differently.
         $categories = array(1 => 'bug', 3 => 'feature', 2 => 'task', 4 => 'support');
         foreach ($issues as $issue) {
             $state = in_array($issue->sid, $open_states) ? 'open' : 'closed';
             // Add to the total count for the category in the values array.
             if (isset($categories[$issue->category])) {
                 $this->currentSample->values[$issue->pid][$categories[$issue->category] . "_{$state}"]++;
             }
         }
         // Clean up potentially huge arrays that are sucking memory.
         unset($issues);
     } else {
         return FALSE;
     }
 }