Преглед изворни кода

CSV import: improved handling of external keys

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@334 a333f486-631f-4898-b8df-5754b55c2be0
romainq пре 15 година
родитељ
комит
e27779ed33
2 измењених фајлова са 138 додато и 36 уклоњено
  1. 136 34
      core/bulkchange.class.inc.php
  2. 2 2
      webservices/import.php

+ 136 - 34
core/bulkchange.class.inc.php

@@ -125,6 +125,32 @@ class CellChangeSpec_Issue extends CellChangeSpec_Modify
 	}
 }
 
+class CellChangeSpec_Ambiguous extends CellChangeSpec_Modify
+{
+	protected $m_iCount;
+	protected $m_sOql;
+
+	public function __construct($previousValue, $iCount, $sOql)
+	{
+		$this->m_iCount = $iCount;
+		$this->m_sQuery = $sOql;
+		parent::__construct(null, $previousValue);
+	}
+
+	public function GetDescription($bHtml = false)
+	{
+		if ($bHtml)
+		{
+			$sCount = '<a href="'.$this->m_sQuery.'">'.$this->m_iCount.'</a>';
+		}
+		else
+		{
+			$sCount = $this->m_iCount;
+		}
+		return "Ambiguous: found $sCount objects";
+	}
+}
+
 
 /**
  * RowStatus
@@ -230,10 +256,10 @@ class BulkChange
 	protected $m_aData; // Note: hereafter, iCol maybe actually be any acceptable key (string)
 	// #@# todo: rename the variables to sColIndex
 	protected $m_aAttList; // attcode => iCol
-	protected $m_aReconcilKeys;// iCol => attcode (attcode = 'id' for the pkey) 
 	protected $m_aExtKeys; // aExtKeys[sExtKeyAttCode][sExtReconcKeyAttCode] = iCol;
+	protected $m_aReconcilKeys;// attcode (attcode = 'id' for the pkey) 
 
-	public function __construct($sClass, $aData, $aAttList, $aReconcilKeys, $aExtKeys)
+	public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys)
 	{
 		$this->m_sClass = $sClass;
 		$this->m_aData = $aData;
@@ -256,6 +282,22 @@ class BulkChange
 		return $oObj;
 	}
 
+	protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
+	{
+		$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
+		$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
+		foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
+		{
+			// The foreign attribute is one of our reconciliation key
+			$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
+			$aResults["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]);
+		}
+
+		$oExtObjects = new CMDBObjectSet($oReconFilter);
+		$aKeys = $oExtObjects->ToArray();
+		return $aKeys;
+	}
+
 	protected function PrepareObject(&$oTargetObj, $aRowData, &$aErrors)
 	{
 		$aResults = array();
@@ -265,6 +307,9 @@ class BulkChange
 		//
 		foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
 		{
+			// Skip external keys used for the reconciliation process
+			if (!array_key_exists($sAttCode, $this->m_aAttList)) continue;
+
 			$oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
 			$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
 			foreach ($aKeyConfig as $sForeignAttCode => $iCol)
@@ -295,7 +340,7 @@ class BulkChange
 			default:
 				$aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches";
 				$previousValue = self::MakeSpecObject($oExtKey->GetTargetClass(), $oTargetObj->Get($sAttCode));
-				$aResults[$sAttCode]= new CellChangeSpec_Issue(null, $previousValue, "Found ".$oExtObjects->Count()." matches");
+				$aResults[$sAttCode]= new CellChangeSpec_Ambiguous($previousValue, $oExtObjects->Count(), $oExtObjects->ToOql());
 			}
 
 			// Report
@@ -473,49 +518,106 @@ class BulkChange
 		foreach($this->m_aData as $iRow => $aRowData)
 		{
 			$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
+			$bSkipQuery = false;
 			foreach($this->m_aReconcilKeys as $sAttCode)
 			{
-				$iCol = $this->m_aAttList[$sAttCode];
-				$oReconciliationFilter->AddCondition($sAttCode, $aRowData[$iCol], '=');
+				$valuecondition = null;
+				if (array_key_exists($sAttCode, $this->m_aExtKeys))
+				{
+					// The value has to be found or verified
+					$aMatches = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
+
+					if (count($aMatches) == 1)
+					{
+						$oRemoteObj = reset($aMatches); // first item
+						$valuecondition = $oRemoteObj->GetKey();
+						$aResult[$iRow][$sAttCode] = new CellChangeSpec_Void($oRemoteObj->GetKey());
+					} 					
+					elseif (count($aMatches) == 0)
+					{
+						$aResult[$iRow]["__RECONCILIATION__"] = "Could not find a match for external key '$sAttCode'";
+						$aResult[$iRow][$sAttCode] = new CellChangeSpec_Issue(null, null, 'object not found');
+					} 					
+					else
+					{
+						$aResult[$iRow]["__RECONCILIATION__"] = "Ambiguous external key '$sAttCode'";
+						$aResult[$iRow][$sAttCode] = new CellChangeSpec_Issue(null, null, 'found '.count($aMatches).' matches');
+					} 					
+				}
+				else
+				{
+					// The value is given in the data row
+					$iCol = $this->m_aAttList[$sAttCode];
+					$valuecondition = $aRowData[$iCol];
+				}
+				if (is_null($valuecondition))
+				{
+					$bSkipQuery = true;
+				}
+				else
+				{
+					$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
+				}
 			}
-			$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
-			switch($oReconciliationSet->Count())
+			if ($bSkipQuery)
 			{
-			case 0:
-				$this->CreateObject($aResult, $iRow, $aRowData, $oChange);
-				// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
-				$aResult[$iRow]["__RECONCILIATION__"] = "Object not found";
-				break;
-			case 1:
-				$oTargetObj = $oReconciliationSet->Fetch();
-				$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
-				$aResult[$iRow]["__RECONCILIATION__"] = "Found a match ".$oTargetObj->GetHyperLink();
-				// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
-				break;
-			default:
-				// Found several matches, ambiguous
-				// Render "void" results on any column
-				foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
+				$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("failed to reconcile");
+			}
+			else
+			{
+				$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
+				switch($oReconciliationSet->Count())
 				{
-					foreach ($aKeyConfig as $sForeignAttCode => $iCol)
+				case 0:
+					$this->CreateObject($aResult, $iRow, $aRowData, $oChange);
+					// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
+					$aResult[$iRow]["__RECONCILIATION__"] = "Object not found";
+					break;
+				case 1:
+					$oTargetObj = $oReconciliationSet->Fetch();
+					$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
+					$aResult[$iRow]["__RECONCILIATION__"] = "Found a match ".$oTargetObj->GetHyperLink();
+					// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
+					break;
+				default:
+					// Found several matches, ambiguous
+					// Render "void" results on any column
+					foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
 					{
-						$aResult[$iRow]["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]);
+						foreach ($aKeyConfig as $sForeignAttCode => $iCol)
+						{
+							$aResult[$iRow]["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]);
+						}
+						$aResult[$iRow][$sAttCode] = new CellChangeSpec_Void('n/a');
 					}
-					$aResult[$iRow][$sAttCode] = new CellChangeSpec_Void('n/a');
-				}
-				foreach ($this->m_aAttList as $sAttCode => $iCol)
-				{
-					$aResult[$iRow]["col$iCol"]= new CellChangeSpec_Void($aRowData[$iCol]);
+					foreach ($this->m_aAttList as $sAttCode => $iCol)
+					{
+						$aResult[$iRow]["col$iCol"]= new CellChangeSpec_Void($aRowData[$iCol]);
+					}
+					$aResult[$iRow]["__RECONCILIATION__"] = "Found ".$oReconciliationSet->Count()." matches";
+					$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("ambiguous reconciliation");
 				}
-				$aResult[$iRow]["__RECONCILIATION__"] = "Found ".$oReconciliationSet->Count()." matches";
-				$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("ambiguous reconciliation");
 			}
 	
 			// Whatever happened, do report the reconciliation values
-			foreach($this->m_aReconcilKeys as $sAttCode)
+			foreach($this->m_aAttList as $iCol)
 			{
-				$iCol = $this->m_aAttList[$sAttCode];
-				$aResult[$iRow]["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]);
+				if (!array_key_exists($iCol, $aResult[$iRow]))
+				{
+					$aResult[$iRow]["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]);
+				}
+			}
+			foreach($this->m_aExtKeys as $sAttCode => $aForeignAtts)
+			{
+				if (!array_key_exists($sAttCode, $aResult[$iRow]))
+				{
+					$aResult[$iRow][$sAttCode] = new CellChangeSpec_Void('n/a');
+					foreach ($aForeignAtts as $sForeignAttCode => $iCol)
+					{
+						// The foreign attribute is one of our reconciliation key
+						$aResult[$iRow]["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]);
+					}
+				}
 			}
 		}
 		return $aResult;

+ 2 - 2
webservices/import.php

@@ -97,8 +97,8 @@ try
 		$sClass,
 		$aData,
 		$aAttList,
-		$aReconcilKeys,
-		$aExtKeys
+		$aExtKeys,
+		$aReconcilKeys
 	);
 
 	$oMyChange = MetaModel::NewObject("CMDBChange");