Index: coder.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/coder/coder.module,v
retrieving revision 1.55.2.19.4.16
diff -u -r1.55.2.19.4.16 coder.module
--- coder.module	23 Jan 2008 12:30:42 -0000	1.55.2.19.4.16
+++ coder.module	30 Jan 2008 00:05:42 -0000
@@ -698,7 +698,18 @@
     // do all of the code reviews
     foreach ($coder_args['#reviews'] as $review) {
       if ($result = do_coder_review($coder_args, $review)) {
-        $results += $result;
+        foreach ($result as $key => $item) {
+          // if we have a statistic field we need to sum the results
+          if ($key == '#stats') {
+            foreach ($result['#stats'] as $severity => $stat) {
+              $results['#stats'][$severity] += $stat;
+            }
+          }
+          // otherwise we can just add the new element to the array
+          else {
+            $results[$key] = $result[$key];
+          }
+        }
       }
     }
 
@@ -1012,7 +1023,7 @@
 }
 
 function do_coder_review($coder_args, $review) {
-  $results = array();
+  $results = array('#stats' => array('minor' => 0, 'normal' => 0, 'critical' => 0));
   if ($review['#rules']) {
     // get the review's severity, used when the rule severity is not defined
     $default_severity = _coder_severity($review['#severity']);
Index: coder_metrics/coder_metrics.module
===================================================================
RCS file: coder_metrics/coder_metrics.module
diff -N coder_metrics/coder_metrics.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ coder_metrics/coder_metrics.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,190 @@
+<?php
+// $Id: $
+
+/** @file
+ * Developer Module that provides summary metrics of code quality
+ */
+
+/**
+ * Implementation of hook_cron().
+ */
+function coder_metrics_cron() {
+  if ($use_cache = variable_get('coder_cache', 1)) {
+    // TODO: move some of the work here... is this really worth it?
+  }
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function coder_metrics_menu($may_cache = true) {
+  $items = array();
+
+  $items[] = array(
+    'path' => 'coder/metrics',
+    'title' => t('Metrics'),
+    'callback' => 'coder_metrics_page',
+    'access' => user_access('view code review'),
+    'type' => MENU_LOCAL_TASK,
+  );
+
+  return $items;
+}
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function coder_metrics_form_alter($form_id, &$form) {
+}
+
+/**
+ * Implementation of code review page
+ */
+function coder_metrics_page() {
+  $output .= drupal_get_form('coder_metrics_page_form');
+  return $output;
+}
+
+function coder_metrics_page_form() {
+  $settings = _coder_get_default_settings();
+  $data = array();
+  
+  // get this once - list of the reviews to perform
+  $reviews = array();
+  $avail_reviews = _coder_reviews();
+  $selected_reviews = $settings['coder_reviews'];
+  foreach ($selected_reviews as $name => $checked) {
+    if ($checked) {
+      $reviews[$name] = $avail_reviews[$name];
+    }
+  }
+
+  if ($coder_form = _coder_settings_form($settings, $system, $files)) {
+    // add style sheet
+    $path = drupal_get_path('module', 'coder');
+    drupal_add_css($path .'/coder.css', 'module');
+
+    // code review non-module core files
+    $module_weight = 0;
+    
+    if (isset($settings['coder_core']) && $settings['coder_core']) {
+      $coder_args = array(
+        '#reviews' => $reviews,
+        '#severity' => $settings['coder_severity'],
+//      '#filename' => $filename,
+      );
+
+      $phpfiles = file_scan_directory('.', '.*\.php', array('.', '..', 'CVS'), 0, false, 'name', 0);
+      _coder_metrics_multifile($data, $coder_args, 'core_php', $phpfiles, 2);
+
+      $includefiles = drupal_system_listing('.*\.inc$', 'includes', 'name', 0);
+      _coder_metrics_multifile($data, $coder_args, 'core_includes', $includefiles, 0);
+    }
+
+    // loop through the selected modules and themes
+    if (isset($system)) {
+      $dups = array(); // used to avoid duplicate includes
+      foreach ($system as $name => $checked) {
+        if ($checked) {
+          // process this one file
+          $filename = $files[$name];
+          if (!$filename) {
+            drupal_set_message(t('Code Review file for %module not found', array('%module' => $name)));
+            continue;
+          }
+          $coder_args = array(
+            '#reviews' => $reviews,
+            '#severity' => $settings['coder_severity'],
+            '#filename' => $filename,
+          );
+          $results = do_coder_reviews($coder_args);
+          $data[$name] = array('name' => array('data' => $name), 'minor' => array('data' => 0), 'normal' => array('data' => 0), 'critical' => array('data' => 0));
+          _coder_metrics_add_stats($data, $results, $name);
+
+          // process the same directory include files
+          if ($settings['coder_includes']) {
+            // NOTE: convert to the realpath here so drupal_system_listing
+            // doesn't return additional paths (i.e., try "module").
+            $path = str_replace('\\', '/', dirname(realpath($filename)));
+            $offset = strpos($path, dirname($filename));
+            if (!isset($dups[$path])) {
+              if (substr($filename, -7) == '.module') {
+                $coder_args['#php_minor'] = 1;
+              }
+              $dups[$path] = 1;
+              $includefiles = drupal_system_listing('.*\.(inc|php|install|schema)$', $path, 'name', 0);
+              $includeresult = _coder_metrics_multifile($data, $coder_args, $name, $includefiles, $offset);
+            }
+          }
+        }
+      }
+
+      uasort($data, "_element_sort");
+      end($data);
+      $key = key($data); // Fetch the last element in the array
+      $max = $data[$key]['#weight'];
+      
+      foreach ($data as $name => $result) {
+        $score = 100 - floor(_coder_metrics_int_divide($data[$name]['#weight'] * 100, $max));
+        $data[$name]['score'] = array('data' => $score . '%');
+        unset($data[$name]['#weight']);
+      }
+      
+      $form[$name]['output'] = array(
+        '#value' => theme('table', array(t('Name'), t('Minor'), t('Normal'), t('Critical'), t('Score')), $data),
+        '#weight' => -1,
+      );
+    }
+  }
+  return $form;
+  
+}
+
+function _coder_metrics_add_stats(&$data, $results, $name) {
+  if ($results['#stats']) {
+    foreach ($results['#stats'] as $severity => $stat) {
+      $data[$name][$severity]['data'] = $data[$name][$severity]['data'] + $stat;
+      $data[$name][$severity]['class'] = 'coder-'. $severity;  
+      $data[$name]['#weight'] = $data[$name]['#weight'] + _coder_metrics_calculate_rating($severity, $stat);
+    }
+  }
+}
+
+/**
+ * Calculates the rating
+ * 
+ * TODO: make the multipliers a setting
+ *
+ * @param string $severity
+ * @param integer $stat
+ * @return integer overall rating vector
+ */
+function _coder_metrics_calculate_rating($severity, $stat) {
+  switch ($severity) {
+    case 'minor':
+      return $stat * 1;
+    case 'normal':
+      return $stat * 5;
+      case 'critical':
+      return $stat * 20;
+  }
+}
+
+function _coder_metrics_multifile(&$data, $coder_args, $name, $files, $offset) {
+  $coder_args['#name'] = $name;
+  foreach ($files as $file) {
+    $filename = drupal_substr($file->filename, $offset);
+    $coder_args['#filename'] = $filename;
+    $results = do_coder_reviews($coder_args);
+    if (empty($data[$name])) {
+      $data[$name] = array('name' => array('data' => $name), 'minor' => array('data' => 0), 'normal' => array('data' => 0), 'critical' => array('data' => 0));
+    }
+    _coder_metrics_add_stats($data, $results, $name);
+  }
+}
+
+function _coder_metrics_int_divide($x, $y) {
+    if ($x == 0) return 0;
+    if ($y == 0) return FALSE;
+    return ($x - ($x % $y)) / $y;
+}
Index: coder_metrics/coder_metrics.info
===================================================================
RCS file: coder_metrics/coder_metrics.info
diff -N coder_metrics/coder_metrics.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ coder_metrics/coder_metrics.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+; $Id$
+name = Coder Metrics
+description = Developer Module that produces metrics for project coding standards compliance
+package = Development
+dependencies = coder
