Add analytics
[bus.git] / busui / owa / modules / base / classes / resultSetManager.php
blob:a/busui/owa/modules/base/classes/resultSetManager.php -> blob:b/busui/owa/modules/base/classes/resultSetManager.php
  <?php
   
  //
  // Open Web Analytics - An Open Source Web Analytics Framework
  //
  // Copyright 2006 Peter Adams. All rights reserved.
  //
  // Licensed under GPL v2.0 http://www.gnu.org/copyleft/gpl.html
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  //
  // $Id$
  //
   
  require_once(OWA_BASE_CLASS_DIR.'pagination.php');
  require_once(OWA_BASE_CLASS_DIR.'timePeriod.php');
  require_once(OWA_DIR.'owa_template.php');
   
  /**
  * Result Set Manager
  *
  * Responsible for creating a data result set from various metrics and dimensions
  *
  * @author Peter Adams <peter@openwebanalytics.com>
  * @copyright Copyright &copy; 2006 Peter Adams <peter@openwebanalytics.com>
  * @license http://www.gnu.org/copyleft/gpl.html GPL v2.0
  * @category owa
  * @package owa
  * @version $Revision$
  * @since owa 1.3.0
  */
   
  class owa_resultSetManager extends owa_base {
   
  /**
  * The params of the caller, either a report or graph
  *
  * @var array
  */
  var $params = array();
   
  /**
  * The lables for calculated measures
  *
  * @var array
  */
  var $labels = array();
   
  /**
  * Data Access Object
  *
  * @var object
  */
  var $db;
   
  /**
  * The dimensions to groupby
  *
  * @var array
  */
  var $dimensions = array();
   
  /**
  * The Number of Dimensions to groupby
  *
  * @var integer
  */
  var $dimensionCount;
   
  /**
  * The table/column or denormalized dimensions
  * associated with this metric
  *
  * @var array
  */
  var $denormalizedDimensions = array();
   
  var $_default_offset = 0;
  var $page;
  var $limit;
  var $order;
  var $format;
  var $constraint_operators = array('==','!=','>=', '<=', '>', '<', '=~', '!~', '=@','!@');
  var $related_entities = array();
  var $related_dimensions = array();
  var $related_metrics = array();
  var $resultSet;
  var $base_table;
  var $metrics = array();
  var $metricsByTable = array();
  var $childMetrics = array();
  var $calculatedMetrics = array();
  var $query_params = array();
  var $baseEntity;
  var $metricObjectsByEntityMap = array();
  var $errors = array();
  var $formatters = array();
   
  function __construct($db = '') {
   
  if ($db) {
  $this->db = $db;
  } else {
  $this->db = owa_coreAPI::dbSingleton();
  }
   
  $this->formatters = array(
  //'yyyymmdd' => array($this, 'dateFormatter'),
  'timestamp' => array($this, 'formatSeconds'),
  'percentage' => array($this, 'formatPercentage'),
  'integer' => array($this, 'numberFormatter'),
  'currency' => array($this, 'formatCurrency')
  );
   
  return parent::__construct();
  }
   
   
  function setConstraint($name, $value, $operator = '') {
   
  if (empty($operator)) {
  $operator = '=';
  }
   
  if (!empty($value)) {
  $this->params['constraints'][] = array('operator' => $operator, 'value' => $value, 'name' => $name);
  }
  }
   
  function setConstraints($array) {
   
  if (is_array($array)) {
   
  if (is_array($this->params['constraints'])) {
  $this->params['constraints'] = array_merge($array, $this->params['constraints']);
  } else {
  $this->params['constraints'] = $array;
  }
  }
  }
   
  function constraintsStringToArray($string) {
   
  if ($string) {
  //print_r($string);
  // add string to query params array for use in URLs.
  $this->query_params['constraints'] = $string;
   
  $constraints = explode(',', $string);
  //print_r($constraints);
  $constraint_array = array();
   
  foreach($constraints as $constraint) {
   
  foreach ($this->constraint_operators as $operator) {
   
  if (strpos($constraint, $operator)) {
  list ($name, $value) = split($operator, $constraint);
   
  $constraint_array[] = array('name' => $name, 'value' => $value, 'operator' => $operator);
   
   
  break;
  }
  }
  }
  //print_r($constraint_array);
  return $constraint_array;
  }
  }
   
  function getConstraints() {
   
  return $this->params['constraints'];
  }
   
  function applyConstraints() {
   
  $nconstraints = array();
   
  foreach ($this->getConstraints() as $k => $constraint) {
   
  $dim = $this->lookupDimension($constraint['name'], $this->baseEntity);
   
  //$dimEntity = owa_coreAPI::entityFactory($dim['entity']);
   
   
  $col = $dim['column'];
  $constraint['name'] = $col;
  $nconstraints[$col] = $constraint;
  $this->db->multiWhere($nconstraints);
  //print_r($nconstraints);
   
  }
   
  }
   
   
  function chooseBaseEntity() {
   
  $metric_imps = array();
   
  // load metric implementations
  foreach ($this->metrics as $metric_name) {
   
  $metric_imps = array_merge($this->getMetricEntities($metric_name), $metric_imps);
   
   
  }
  //print_r($metric_imps);
  owa_coreAPI::debug('pre-reduce set of entities to choose from: '.print_r($metric_imps, true));
   
  $entities = array();
  // reduce entities
  foreach ($metric_imps as $mimp) {
   
  if (empty($entities)) {
  $entities = $mimp;
  }
   
  $entities = $this->reduceTables($mimp, $entities);
   
  if (empty($entities)) {
  return $this->addError('illegal metric combination');
  }
  }
   
  owa_coreAPI::debug('post-reduce set of entities to choose from: '.print_r($entities, true));
   
  // check summary level of entities
  $niceness = array();
   
  foreach ($entities as $entity) {
   
  $niceness[$entity] = owa_coreAPI::entityFactory($entity)->getSummaryLevel();
  }
  //sort by summary level
  arsort($niceness);
   
  owa_coreAPI::debug('Entities summary levels: '.print_r($niceness, true));
   
  $entity_count = count($niceness);
  $i = 1;
  //check entities for dimension relations
  foreach ($niceness as $entity_name => $summary_level) {
   
  $error = false;
   
  //cycle through each dimension frm dim list and those found in constraints.
  $dims = array_unique(array_merge($this->dimensions, $this->getDimensionsFromConstraints()));
   
  owa_coreAPI::debug(sprintf('Dimensions: %s',print_r($this->dimensions, true)));
   
  owa_coreAPI::debug(sprintf('Checking the following dimensions for relation to %s: %s',$entity_name, print_r($dims, true)));
   
  foreach ($dims as $dimension) {
   
  $check = $this->isDimensionRelated($dimension, $entity_name);
   
  // is the realtionship check fails then move onto the next entity.
  if (!$check) {
  $error = true;
  owa_coreAPI::debug("$dimension is not related to $entity_name. Moving on to next entity...");
  break;
  } else {
  owa_coreAPI::debug("Dimension: $dimension is related to $entity_name.");
  }
  }
   
  // is no error then everythig is related and we are good to go.
  if (!$error) {
  owa_coreAPI::debug('optimal base entity is: '.$entity_name);
  $this->baseEntity = owa_coreAPI::entityFactory($entity_name);
  return $this->baseEntity;
  }
   
  if ($i === $entity_count) {
  $this->addError('illegal dimension combination: '.$dimension);
  } else {
  $i++;
  }
  }
  }
   
  function getDimensionsFromConstraints() {
   
  $dims = array();
   
  $constraints = $this->getConstraints();
  //print_r($constraints);
  if (!empty($constraints)) {
   
  foreach ($constraints as $carray) {
   
  $dims[] = $carray['name'];
  }
  }
   
  return $dims;
  }
   
  function isDimensionRelated($dimension_name, $entity_name) {
   
  $entity = owa_coreAPI::entityFactory($entity_name);
   
  $dimension = $this->lookupDimension($dimension_name, $entity);
   
  if ($dimension['denormalized'] === true) {
  $this->related_dimensions[$dimension['name']] = $dimension;
  owa_coreAPI::debug("Dimension: $dimension_name is denormalized into $entity_name");
  return true;
  } else {
   
  $fk = $this->getDimensionForeignKey($dimension, $entity);
   
  if ($fk) {
  owa_coreAPI::debug("Dimension: $dimension_name is related to $entity_name");
  $this->related_dimensions[$dimension['name']] = $dimension;
  return true;
  } else {
  owa_coreAPI::debug("Could not find a foreign key for $dimension_name in $entity_name");
  }
  }
  }
   
  function getMetricEntities($metric_name) {
   
  //get the class implementations
  $s = owa_coreAPI::serviceSingleton();
  $classes = $s->getMetricClasses($metric_name);
   
  $entities = array();
   
  // cycles through metric classes and get their entity names
  foreach ($classes as $name => $map) {
  $m = owa_coreAPI::metricFactory($map['class'], $map['params']);
   
  // check to see if this is a calculated metric
  if ($m->isCalculated()) {
   
  foreach ($m->getChildMetrics() as $cmetric_name) {
  $this->addCalculatedMetric($m);
  $entities = array_merge($this->getMetricEntities($cmetric_name), $entities);
  }
   
  } else {
  $this->metricObjectsByEntityMap[$m->getEntityName()][$metric_name] = $m;
  $entities[$metric_name][] = $m->getEntityName();
  }
   
  }
   
  return $entities;
  }
   
  function reduceTables($new, $old) {
   
  return array_intersect($new, $old);
  }
   
  function getDimensionForeignKey($dimension, $entity) {
   
  if ($dimension) {
  //$entity = ;
  $dim = $dimension;
  $fk = array();
  // check for foreign key column by name if dimension specifies one
  if (array_key_exists('foreign_key_name', $dim) && !empty($dim['foreign_key_name'])) {
  // get foreign key col by
  if ($entity->isForeignKeyColumn($dim['foreign_key_name'])){
  $fk = array('col' => $dim['foreign_key_name'], 'entity' => $entity);
  }
   
  } else {
  // if not check for foreign key by entity name
  //check to see if the metric's entity has a foreign key to the dimenesion table.
  $fk = array();
   
  $fkcol = $entity->getForeignKeyColumn($dim['entity']);
  owa_coreAPI::debug("Foreign Key check: ". print_r($fkcol, true));
  if ($fkcol) {
  $fk['col'] = $fkcol;
  $fk['entity'] = $entity;
  }
  }
 </