Stop watches - beta (change tracking to be reviewed)

SVN:trunk[2166]
This commit is contained in:
Romain Quetiez
2012-09-07 12:51:26 +00:00
parent 2d9193e60e
commit f05f5865e9
6 changed files with 536 additions and 146 deletions

View File

@@ -132,6 +132,22 @@ abstract class AttributeDefinition
return $this->m_sHostClass;
}
public function ListSubItems()
{
$aSubItems = array();
foreach(MetaModel::ListAttributeDefs($this->m_sHostClass) as $sAttCode => $oAttDef)
{
if ($oAttDef instanceof AttributeSubItem)
{
if ($oAttDef->Get('target_attcode') == $this->m_sCode)
{
$aSubItems[$sAttCode] = $oAttDef;
}
}
}
return $aSubItems;
}
// Note: I could factorize this code with the parameter management made for the AttributeDef class
// to be overloaded
static public function ListExpectedParams()
@@ -174,6 +190,8 @@ abstract class AttributeDefinition
public function IsHierarchicalKey() {return false;}
public function IsExternalField() {return false;}
public function IsWritable() {return false;}
public function LoadInObject() {return true;}
public function GetValue($oHostObject){return null;} // must return the value if LoadInObject returns false
public function IsNullAllowed() {return true;}
public function GetCode() {return $this->m_sCode;}
@@ -389,16 +407,25 @@ abstract class AttributeDefinition
return (string)$sValue;
}
/**
* Override to display the value in the GUI
*/
public function GetAsHTML($sValue, $oHostObject = null)
{
return Str::pure2html((string)$sValue);
}
/**
* Override to export the value in XML
*/
public function GetAsXML($sValue, $oHostObject = null)
{
return Str::pure2xml((string)$sValue);
}
/**
* Override to escape the value when read by DBObject::GetAsCSV()
*/
public function GetAsCSV($sValue, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
{
return (string)$sValue;
@@ -2432,7 +2459,7 @@ class AttributeDuration extends AttributeInteger
return Str::pure2html(self::FormatDuration($value));
}
static function FormatDuration($duration)
public static function FormatDuration($duration)
{
$aDuration = self::SplitDuration($duration);
$sResult = '';
@@ -2507,12 +2534,18 @@ class AttributeDate extends AttributeDateTime
class AttributeDeadline extends AttributeDateTime
{
public function GetAsHTML($value, $oHostObject = null)
{
$sResult = self::FormatDeadline($value);
return $sResult;
}
public static function FormatDeadline($value)
{
$sResult = '';
if ($value !== null)
{
$iValue = AttributeDateTime::GetAsUnixSeconds($value);
$sDate = parent::GetAsHTML($value, $oHostObject);
$sDate = $value;
$difference = $iValue - time();
if ($difference >= 0)
@@ -2526,6 +2559,7 @@ class AttributeDeadline extends AttributeDateTime
$sFormat = MetaModel::GetConfig()->Get('deadline_format', '$difference$');
$sResult = str_replace(array('$date$', '$difference$'), array($sDate, $sDifference), $sFormat);
}
return $sResult;
}
@@ -3234,7 +3268,11 @@ class AttributeStopWatch extends AttributeDefinition
public function IsScalar() {return true;}
public function IsWritable() {return false;}
public function GetDefaultValue() {return $this->NewStopWatch();}
//public function IsNullAllowed() {return $this->GetOptional("is_null_allowed", false);}
public function GetEditValue($value, $oHostObj = null)
{
return $value->GetTimeSpent();
}
public function GetStates()
{
@@ -3264,6 +3302,24 @@ class AttributeStopWatch extends AttributeDefinition
return $proposedValue;
}
public function Equals($val1, $val2)
{
if ($val1 === $val2) return true;
if (is_object($val1) != is_object($val2))
{
return false;
}
if (!is_object($val1))
{
// string ?
return false;
}
// Both values are Object sets
return $val1->HasSameContents($val2);
}
public function GetSQLExpressions($sPrefix = '')
{
if ($sPrefix == '')
@@ -3281,6 +3337,7 @@ class AttributeStopWatch extends AttributeDefinition
$sThPrefix = '_'.$iThreshold;
$aColumns[$sThPrefix.'_deadline'] = $sPrefix.$sThPrefix.'_deadline';
$aColumns[$sThPrefix.'_passed'] = $sPrefix.$sThPrefix.'_passed';
$aColumns[$sThPrefix.'_triggered'] = $sPrefix.$sThPrefix.'_triggered';
$aColumns[$sThPrefix.'_overrun'] = $sPrefix.$sThPrefix.'_overrun';
}
return $aColumns;
@@ -3314,6 +3371,7 @@ class AttributeStopWatch extends AttributeDefinition
$sThPrefix = '_'.$iThreshold;
$aExpectedCols[] = $sPrefix.$sThPrefix.'_deadline';
$aExpectedCols[] = $sPrefix.$sThPrefix.'_passed';
$aExpectedCols[] = $sPrefix.$sThPrefix.'_triggered';
$aExpectedCols[] = $sPrefix.$sThPrefix.'_overrun';
}
foreach ($aExpectedCols as $sExpectedCol)
@@ -3340,6 +3398,7 @@ class AttributeStopWatch extends AttributeDefinition
$iThreshold,
self::DateToSeconds($aCols[$sPrefix.$sThPrefix.'_deadline']),
(bool)($aCols[$sPrefix.$sThPrefix.'_passed'] == 1),
(bool)($aCols[$sPrefix.$sThPrefix.'_triggered'] == 1),
$aCols[$sPrefix.$sThPrefix.'_overrun']
);
}
@@ -3362,6 +3421,7 @@ class AttributeStopWatch extends AttributeDefinition
$sPrefix = $this->GetCode().'_'.$iThreshold;
$aValues[$sPrefix.'_deadline'] = self::SecondsToDate($value->GetThresholdDate($iThreshold));
$aValues[$sPrefix.'_passed'] = $value->IsThresholdPassed($iThreshold) ? '1' : '0';
$aValues[$sPrefix.'_triggered'] = $value->IsThresholdTriggered($iThreshold) ? '1' : '0';
$aValues[$sPrefix.'_overrun'] = $value->GetOverrun($iThreshold);
}
}
@@ -3388,6 +3448,7 @@ class AttributeStopWatch extends AttributeDefinition
$sPrefix = $this->GetCode().'_'.$iThreshold;
$aColumns[$sPrefix.'_deadline'] = 'DATETIME NULL';
$aColumns[$sPrefix.'_passed'] = 'TINYINT(1) NULL';
$aColumns[$sPrefix.'_triggered'] = 'TINYINT(1) NULL';
$aColumns[$sPrefix.'_overrun'] = 'INT(11) UNSIGNED NULL';
}
return $aColumns;
@@ -3395,8 +3456,6 @@ class AttributeStopWatch extends AttributeDefinition
public function GetFilterDefinitions()
{
//return array();
// still not working... see later...
$aRes = array(
$this->GetCode() => new FilterFromAttribute($this),
$this->GetCode().'_started' => new FilterFromAttribute($this, '_started'),
@@ -3408,6 +3467,7 @@ class AttributeStopWatch extends AttributeDefinition
$sPrefix = $this->GetCode().'_'.$iThreshold;
$aRes[$sPrefix.'_deadline'] = new FilterFromAttribute($this, '_deadline');
$aRes[$sPrefix.'_passed'] = new FilterFromAttribute($this, '_passed');
$aRes[$sPrefix.'_triggered'] = new FilterFromAttribute($this, '_triggered');
$aRes[$sPrefix.'_overrun'] = new FilterFromAttribute($this, '_overrun');
}
return $aRes;
@@ -3449,6 +3509,288 @@ class AttributeStopWatch extends AttributeDefinition
{
return $this->Get('thresholds');
}
/**
* To expose internal values: Declare an attribute AttributeSubItem
* and implement the GetSubItemXXXX verbs
*/
public function GetSubItemSQLExpression($sItemCode)
{
$sPrefix = $this->GetCode();
switch($sItemCode)
{
case 'timespent':
return array('' => $sPrefix.'_timespent');
case 'started':
return array('' => $sPrefix.'_started');
case 'laststart':
return array('' => $sPrefix.'_laststart');
case 'stopped':
return array('' => $sPrefix.'_stopped');
}
foreach ($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch($sThresholdCode)
{
case 'deadline':
return array('' => $sPrefix.'_'.$iThreshold.'_deadline');
case 'passed':
return array('' => $sPrefix.'_'.$iThreshold.'_passed');
case 'triggered':
return array('' => $sPrefix.'_'.$iThreshold.'_triggered');
case 'overrun':
return array('' => $sPrefix.'_'.$iThreshold.'_overrun');
}
}
}
throw new CoreException("Unknown item code '$sItemCode' for attribute ".$this->GetHostClass().'::'.$this->GetCode());
}
public function GetSubItemValue($sItemCode, $value, $oHostObject = null)
{
$oStopWatch = $value;
switch($sItemCode)
{
case 'timespent':
return $oStopWatch->GetTimeSpent();
case 'started':
return $oStopWatch->GetStartDate();
case 'laststart':
return $oStopWatch->GetLastStartDate();
case 'stopped':
return $oStopWatch->GetStopDate();
}
foreach ($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch($sThresholdCode)
{
case 'deadline':
return $oStopWatch->GetThresholdDate($iThreshold);
case 'passed':
return $oStopWatch->IsThresholdPassed($iThreshold);
case 'triggered':
return $oStopWatch->IsThresholdTriggered($iThreshold);
case 'overrun':
return $oStopWatch->GetOverrun($iThreshold);
}
}
}
throw new CoreException("Unknown item code '$sItemCode' for attribute ".$this->GetHostClass().'::'.$this->GetCode());
}
static protected function GetDateFormat($bFull = false)
{
if ($bFull)
{
return "Y-m-d H:i:s";
}
else
{
return "Y-m-d H:i";
}
}
public function GetSubItemAsHTML($sItemCode, $value)
{
$sHtml = $value;
switch($sItemCode)
{
case 'timespent':
$sHtml = Str::pure2html(AttributeDuration::FormatDuration($value));
break;
case 'started':
case 'laststart':
case 'stopped':
if (is_null($value))
{
$sHtml = ''; // Undefined
}
else
{
$sHtml = date(self::GetDateFormat(), $value);
}
break;
default:
foreach ($this->ListThresholds() as $iThreshold => $aFoo)
{
$sThPrefix = $iThreshold.'_';
if (substr($sItemCode, 0, strlen($sThPrefix)) == $sThPrefix)
{
// The current threshold is concerned
$sThresholdCode = substr($sItemCode, strlen($sThPrefix));
switch($sThresholdCode)
{
case 'deadline':
if ($value)
{
$sDate = date(self::GetDateFormat(true /*full*/), $value);
$sHtml = Str::pure2html(AttributeDeadline::FormatDeadline($sDate));
}
else
{
$sHtml = '';
}
break;
case 'passed':
$sHtml = $value ? '1' : '0';
break;
case 'triggered':
$sHtml = $value ? '1' : '0';
break;
case 'overrun':
$sHtml = Str::pure2html(AttributeDuration::FormatDuration($value));
break;
}
}
}
}
return $sHtml;
}
public function GetSubItemAsCSV($sItemCode, $value, $sSeparator = ',', $sTextQualifier = '"')
{
return $value;
}
public function GetSubItemAsXML($sItemCode, $value)
{
return Str::pure2xml((string)$value);
}
}
/**
* View of a subvalue of another attribute
* If an attribute implements the verbs GetSubItem.... then it can expose
* internal values, each of them being an attribute and therefore they
* can be displayed at different times in the object lifecycle, and used for
* reporting (as a condition in OQL, or as an additional column in an export)
* Known usages: Stop Watches can expose threshold statuses
*/
class AttributeSubItem extends AttributeDefinition
{
static public function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array('target_attcode', 'item_code'));
}
public function GetParentAttCode() {return $this->Get("target_attcode");}
/**
* Helper : get the attribute definition to which the execution will be forwarded
*/
protected function GetTargetAttDef()
{
$sClass = $this->GetHostClass();
$oParentAttDef = MetaModel::GetAttributeDef($sClass, $this->Get('target_attcode'));
return $oParentAttDef;
}
public function GetEditClass() {return "";}
public function GetValuesDef() {return null;}
//public function GetPrerequisiteAttributes() {return $this->Get("depends_on");}
public function IsDirectField() {return true;}
public function IsScalar() {return true;}
public function IsWritable() {return false;}
public function GetDefaultValue() {return null;}
// public function IsNullAllowed() {return false;}
public function LoadInObject() {return false;} // if this verb returns true, then GetValue must be implemented
//
// protected function ScalarToSQL($value) {return $value;} // format value as a valuable SQL literal (quoted outside)
public function FromSQLToValue($aCols, $sPrefix = '')
{
}
public function GetSQLColumns()
{
return array();
}
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
}
public function GetBasicFilterOperators()
{
return array();
}
public function GetBasicFilterLooseOperator()
{
return "=";
}
public function GetBasicFilterSQLExpr($sOpCode, $value)
{
$sQValue = CMDBSource::Quote($value);
switch ($sOpCode)
{
case '!=':
return $this->GetSQLExpr()." != $sQValue";
break;
case '=':
default:
return $this->GetSQLExpr()." = $sQValue";
}
}
public function GetSQLExpressions($sPrefix = '')
{
$oParent = $this->GetTargetAttDef();
$res = $oParent->GetSubItemSQLExpression($this->Get('item_code'));
return $res;
}
/**
* Used by DBOBject::Get()
*/
public function GetValue($parentValue, $oHostObject = null)
{
$oParent = $this->GetTargetAttDef();
$res = $oParent->GetSubItemValue($this->Get('item_code'), $parentValue, $oHostObject);
return $res;
}
public function GetAsHTML($value, $oHostObject = null)
{
$oParent = $this->GetTargetAttDef();
$res = $oParent->GetSubItemAsHTML($this->Get('item_code'), $value);
return $res;
}
public function GetAsCSV($value, $sSeparator = ',', $sTextQualifier = '"', $oHostObject = null)
{
$oParent = $this->GetTargetAttDef();
$res = $oParent->GetSubItemAsCSV($this->Get('item_code'), $value, $sSeparator = ',', $sTextQualifier = '"');
return $res;
}
public function GetAsXML($value, $oHostObject = null)
{
$oParent = $this->GetTargetAttDef();
$res = $oParent->GetSubItemAsXML($this->Get('item_code'), $value);
return $res;
}
}
/**

View File

@@ -133,8 +133,18 @@ abstract class CMDBObject extends DBObject
foreach ($aValues as $sAttCode=> $value)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsExternalField()) continue; // #@# temporary
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
{
$original = null;
}
if ($oAttDef instanceOf AttributeOneWayPassword)
{
// One Way encrypted passwords' history is stored -one way- encrypted
@@ -144,11 +154,7 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
if (is_null($original))
{
$original = '';
}
@@ -164,11 +170,7 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
if (is_null($original))
{
$original = '';
}
@@ -184,11 +186,7 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
}
else
if (is_null($original))
{
$original = new ormDocument();
}
@@ -197,26 +195,30 @@ abstract class CMDBObject extends DBObject
}
elseif ($oAttDef instanceOf AttributeStopWatch)
{
// Stop watches
// TEMPORARY IMPLEMENTATION
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
//
if (is_null($original))
{
$original = new OrmStopWatch();
}
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
{
$item_value = $oSubItemAttDef->GetValue($value);
$item_original = $oSubItemAttDef->GetValue($original);
// Temporary - working thanks to ormStopWatch::__toString()
if (array_key_exists($sAttCode, $aOrigValues))
{
$sOriginalValue = $aOrigValues[$sAttCode];
if ($item_value != $item_original)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
$oMyChangeOp->Set("oldvalue", $item_original);
$oMyChangeOp->Set("newvalue", $item_value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
else
{
$sOriginalValue = 'undefined';
}
$oMyChangeOp->Set("oldvalue", $sOriginalValue);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeCaseLog)
{
@@ -238,17 +240,9 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
if (!is_null($original) && ($original instanceof ormCaseLog))
{
$original = $aOrigValues[$sAttCode];
if ($original instanceof ormCaseLog)
{
$original = $original->GetText();
}
}
else
{
$original = null;
$original = $original->GetText();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
@@ -262,16 +256,11 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (array_key_exists($sAttCode, $aOrigValues))
if (is_null($original))
{
$sOriginalValue = $aOrigValues[$sAttCode];
$original = 'undefined';
}
else
{
$sOriginalValue = 'undefined';
}
$oMyChangeOp->Set("oldvalue", $sOriginalValue);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}

View File

@@ -145,6 +145,7 @@ abstract class DBObject
{
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
{
if (!$oAttDef->LoadInObject()) continue;
if (!isset($this->m_aLoadedAtt[$sAttCode]) || !$this->m_aLoadedAtt[$sAttCode])
{
return false;
@@ -248,6 +249,8 @@ abstract class DBObject
// Skip links (could not be loaded by the mean of this query)
if ($oAttDef->IsLinkSet()) continue;
if (!$oAttDef->LoadInObject()) continue;
// Note: we assume that, for a given attribute, if it can be loaded,
// then one column will be found with an empty suffix, the others have a suffix
// Take care: the function isset will return false in case the value is null,
@@ -411,68 +414,73 @@ abstract class DBObject
public function GetStrict($sAttCode)
{
if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this))))
{
throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this));
}
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if (isset($this->m_aLoadedAtt[$sAttCode]))
if (!$oAttDef->LoadInObject())
{
// Standard case... we have the information directly
}
elseif ($this->m_bIsInDB && !$this->m_bDirty)
{
// Lazy load (polymorphism): complete by reloading the entire object
// #@# non-scalar attributes.... handle that differently?
$this->Reload();
}
elseif ($sAttCode == 'friendlyname')
{
// The friendly name is not computed and the object is dirty
// Todo: implement the computation of the friendly name based on sprintf()
//
$this->m_aCurrValues[$sAttCode] = '';
$sParentAttCode = $oAttDef->GetParentAttCode();
$parentValue = $this->GetStrict($sParentAttCode);
$value = $oAttDef->GetValue($parentValue, $this);
}
else
{
// Not loaded... is it related to an external key?
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
if (isset($this->m_aLoadedAtt[$sAttCode]))
{
// Let's get the object and compute all of the corresponding attributes
// (i.e not only the requested attribute)
//
$sExtKeyAttCode = $oAttDef->GetKeyAttCode();
if ($iRemote = $this->Get($sExtKeyAttCode))
// Standard case... we have the information directly
}
elseif ($this->m_bIsInDB && !$this->m_bDirty)
{
// Lazy load (polymorphism): complete by reloading the entire object
// #@# non-scalar attributes.... handle that differently?
$this->Reload();
}
elseif ($sAttCode == 'friendlyname')
{
// The friendly name is not computed and the object is dirty
// Todo: implement the computation of the friendly name based on sprintf()
//
$this->m_aCurrValues[$sAttCode] = '';
}
else
{
// Not loaded... is it related to an external key?
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
{
$oExtKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
$oRemote = MetaModel::GetObject($oExtKeyAttDef->GetTargetClass(), $iRemote);
}
else
{
$oRemote = null;
}
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
{
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sExtKeyAttCode))
// Let's get the object and compute all of the corresponding attributes
// (i.e not only the requested attribute)
//
$sExtKeyAttCode = $oAttDef->GetKeyAttCode();
if ($iRemote = $this->Get($sExtKeyAttCode))
{
if ($oRemote)
$oExtKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
$oRemote = MetaModel::GetObject($oExtKeyAttDef->GetTargetClass(), $iRemote);
}
else
{
$oRemote = null;
}
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
{
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sExtKeyAttCode))
{
$this->m_aCurrValues[$sCode] = $oRemote->Get($oDef->GetExtAttCode());
if ($oRemote)
{
$this->m_aCurrValues[$sCode] = $oRemote->Get($oDef->GetExtAttCode());
}
else
{
$this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue();
}
$this->m_aLoadedAtt[$sCode] = true;
}
else
{
$this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue();
}
$this->m_aLoadedAtt[$sCode] = true;
}
}
}
$value = $this->m_aCurrValues[$sAttCode];
}
$value = $this->m_aCurrValues[$sAttCode];
if ($value instanceof DBObjectSet)
{
$value->Rewind();

View File

@@ -2365,6 +2365,7 @@ abstract class MetaModel
foreach ($aAttList as $sAttCode => $oAttDef)
{
if (!$oAttDef->IsScalar()) continue;
// keep because it can be used for sorting - if (!$oAttDef->LoadInObject()) continue;
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
{

View File

@@ -54,23 +54,47 @@ class ormStopWatch
$this->aThresholds = array();
}
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $iOverrun = 0)
// BUGGY - DOES NOT DETECT A CHANGE IN THE DEADLINE
//
public function HasSameContents($oStopWatch)
{
if ($oStopWatch->iTimeSpent != $this->iTimeSpent) return false;
if ($oStopWatch->iStarted != $this->iStarted) return false;
if ($oStopWatch->iLastStart != $this->iLastStart) return false;
if ($oStopWatch->iStopped != $this->iStopped) return false;
if ($oStopWatch->aThresholds != $this->aThresholds) return false;
// Array comparison is not recursive... let's do it by myself
foreach ($oStopWatch->aThresholds as $iPercent => $aThresholdData)
{
// Assumption: the thresholds will not change dynamically (defined at application design time)
$aThisThresholdData = $this->aThresholds[$iPercent];
if ($aThisThresholdData['deadline'] != $aThresholdData['deadline']) return false;
if ($aThisThresholdData['passed'] != $aThresholdData['passed']) return false;
if ($aThisThresholdData['triggered'] != $aThresholdData['triggered']) return false;
if ($aThisThresholdData['overrun'] != $aThresholdData['overrun']) return false;
}
return false;
return true;
}
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = 0)
{
$this->aThresholds[$iPercent] = array(
'deadline' => $tDeadline, // unix time (seconds)
'passed' => $bPassed,
'triggered' => $bTriggered,
'overrun' => $iOverrun
);
}
public function MarkThresholdAsPassed($iPercent)
public function MarkThresholdAsTriggered($iPercent)
{
$this->aThresholds[$iPercent]['passed'] = true;
}
public function __toString()
{
return (string)$this->iTimeSpent;
$this->aThresholds[$iPercent]['triggered'] = true;
}
public function GetTimeSpent()
@@ -127,6 +151,17 @@ class ormStopWatch
return false;
}
}
public function IsThresholdTriggered($iPercent)
{
if (array_key_exists($iPercent, $this->aThresholds))
{
return $this->aThresholds[$iPercent]['triggered'];
}
else
{
return false;
}
}
public function GetAsHTML($oAttDef, $oHostObject = null)
{
@@ -157,21 +192,20 @@ class ormStopWatch
foreach ($this->aThresholds as $iPercent => $aThresholdData)
{
if ($aThresholdData['passed'])
$sThresholdDesc = $oAttDef->SecondsToDate($aThresholdData['deadline']);
if ($aThresholdData['triggered'])
{
if ($aThresholdData['overrun'])
{
$aProperties[$iPercent.'%'] = $oAttDef->SecondsToDate($aThresholdData['deadline'])." <b>PASSED</b> by ".$aThresholdData['overrun']." seconds";
$sThresholdDesc .= " <b>TRIGGERED</b>, Overrun:".(int) $aThresholdData['overrun']." seconds";
}
else
{
$aProperties[$iPercent.'%'] = $oAttDef->SecondsToDate($aThresholdData['deadline'])." <b>PASSED</b>";
// Still active, overrun unknown
$sThresholdDesc .= " <b>TRIGGERED</b>";
}
}
else
{
$aProperties[$iPercent.'%'] = $oAttDef->SecondsToDate($aThresholdData['deadline']);
}
$aProperties[$iPercent.'%'] = $sThresholdDesc;
}
$sRes = "<TABLE class=\"listResults\">";
$sRes .= "<TBODY>";
@@ -247,6 +281,7 @@ class ormStopWatch
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
{
$aThresholdData['passed'] = false;
$aThresholdData['triggered'] = false;
$aThresholdData['deadline'] = null;
$aThresholdData['overrun'] = null;
}
@@ -289,21 +324,31 @@ class ormStopWatch
$iDurationGoal = $this->ComputeGoal($oObject, $oAttDef);
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
{
if (!$aThresholdData['passed'])
if (is_null($iDurationGoal))
{
if (is_null($iDurationGoal))
{
// No limit: leave null thresholds
$aThresholdData['deadline'] = null;
}
else
{
$iThresholdDuration = round($iPercent * $iDurationGoal / 100);
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration);
}
// No limit: leave null thresholds
$aThresholdData['deadline'] = null;
}
else
{
$iThresholdDuration = round($iPercent * $iDurationGoal / 100);
$aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iLastStart, $iThresholdDuration - $this->iTimeSpent);
// OR $aThresholdData['deadline'] = $this->ComputeDeadline($oObject, $oAttDef, $this->iStarted, $iThresholdDuration);
}
if (is_null($aThresholdData['deadline']) || ($aThresholdData['deadline'] > time()))
{
// The threshold is in the future, reset
$aThresholdData['passed'] = false;
$aThresholdData['triggered'] = false;
}
else
{
// The new threshold is in the past
$aThresholdData['passed'] = true;
}
}
return true;
}
@@ -323,21 +368,21 @@ class ormStopWatch
foreach ($this->aThresholds as $iPercent => &$aThresholdData)
{
if ($aThresholdData['passed'])
if (!is_null($aThresholdData['deadline']) && (time() > $aThresholdData['deadline']))
{
if (is_null($aThresholdData['overrun']))
{
// First stop after the deadline is passed
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time());
$aThresholdData['overrun'] = $iOverrun;
}
else
$aThresholdData['passed'] = true;
if ($aThresholdData['overrun'] > 0)
{
// Accumulate from last start
$aThresholdData['overrun'] += $iElapsed;
}
else
{
// First stop after the deadline has been passed
$iOverrun = $this->ComputeDuration($oObject, $oAttDef, $aThresholdData['deadline'], time());
$aThresholdData['overrun'] = $iOverrun;
}
}
$aThresholdData['deadline'] = null;
}
$this->iLastStart = null;
@@ -372,8 +417,8 @@ class CheckStopWatchThresholds implements iBackgroundProcess
{
$iPercent = $aThresholdData['percent']; // could be different than the index !
$sExpression = "SELECT $sClass WHERE {$sAttCode}_{$iThreshold}_passed = 0 AND {$sAttCode}_{$iThreshold}_deadline < NOW()";
echo $sExpression."<br/>\n";
$sExpression = "SELECT $sClass WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < NOW()";
//echo $sExpression."<br/>\n";
$oFilter = DBObjectSearch::FromOQL($sExpression);
$aList = array();
$oSet = new DBObjectSet($oFilter);
@@ -382,7 +427,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
$sClass = get_class($oObj);
$aList[] = $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold;
echo $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold."<br/>\n";
//echo $sClass.'::'.$oObj->GetKey().' '.$sAttCode.' '.$iThreshold."\n";
// Execute planned actions
//
@@ -396,10 +441,10 @@ class CheckStopWatchThresholds implements iBackgroundProcess
call_user_func_array($aCallSpec, $aParams);
}
// Mark the threshold as "passed"
// Mark the threshold as "triggered"
//
$oSW = $oObj->Get($sAttCode);
$oSW->MarkThresholdAsPassed($iThreshold);
$oSW->MarkThresholdAsTriggered($iThreshold);
$oObj->Set($sAttCode, $oSW);
if($oObj->IsModified())
@@ -407,7 +452,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
// Todo - factorize so that only one single change will be instantiated
$oMyChange = new CMDBChange();
$oMyChange->Set("date", time());
$oMyChange->Set("userinfo", "Automatic - threshold passed");
$oMyChange->Set("userinfo", "Automatic - threshold triggered");
$iChangeId = $oMyChange->DBInsertNoReload();
$oObj->DBUpdateTracked($oMyChange, true /*skip security*/);
@@ -432,7 +477,7 @@ class CheckStopWatchThresholds implements iBackgroundProcess
}
$iProcessed = count($aList);
return "Encountered $iProcessed passed threshold(s)";
return "Triggered $iProcessed threshold(s)";
}
}

View File

@@ -724,6 +724,11 @@ EOF;
}
$aParameters['thresholds'] = 'array('.implode(', ', $aThresholds).')';
}
elseif ($sAttType == 'AttributeSubItem')
{
$aParameters['target_attcode'] = $this->GetPropString($oField, 'target_attcode');
$aParameters['item_code'] = $this->GetPropString($oField, 'item_code');
}
else
{
$aParameters['allowed_values'] = 'null'; // or "new ValueSetEnum('SELECT xxxx')"