main.itop-tickets.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. // Copyright (C) 2010-2012 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * Base class for computing TTO or TTR on a ticket
  20. */
  21. class ResponseTicketSLT
  22. {
  23. /**
  24. * Determines the shortest SLT, for this ticket, for the given metric. Returns null is no SLT was found
  25. * @param string $sMetric Type of metric 'TTO', 'TTR', etc as defined in the SLT class
  26. * @return hash Array with 'SLT' => name of the SLT selected, 'value' => duration in seconds of the SLT metric, null if no SLT applies to this ticket
  27. */
  28. protected static function ComputeSLT($oTicket, $sMetric = 'TTO')
  29. {
  30. $iDeadline = null;
  31. if (MetaModel::IsValidClass('SLT'))
  32. {
  33. $sType=get_class($oTicket);
  34. if ($sType == 'Incident')
  35. {
  36. $sRequestType = 'incident';
  37. }
  38. else
  39. {
  40. $sRequestType = $oTicket->Get('request_type');
  41. }
  42. $aArgs = $oTicket->ToArgs();
  43. $aArgs['metric'] = $sMetric;
  44. $aArgs['request_type'] = $sRequestType;
  45. //echo "<p>Managing:".$sMetric."-".$this->Get('request_type')."-".$this->Get('importance')."</p>\n";
  46. $oSLTSet = new DBObjectSet(DBObjectSearch::FromOQL(RESPONSE_TICKET_SLT_QUERY),
  47. array(),
  48. $aArgs
  49. );
  50. $iMinDuration = PHP_INT_MAX;
  51. $sSLTName = '';
  52. while($oSLT = $oSLTSet->Fetch())
  53. {
  54. $iDuration = (int)$oSLT->Get('value');
  55. $sUnit = $oSLT->Get('unit');
  56. switch($sUnit)
  57. {
  58. case 'days':
  59. $iDuration = $iDuration * 24; // 24 hours in 1 days
  60. // Fall though
  61. case 'hours':
  62. $iDuration = $iDuration * 60; // 60 minutes in 1 hour
  63. // Fall though
  64. case 'minutes':
  65. $iDuration = $iDuration * 60;
  66. }
  67. if ($iDuration < $iMinDuration)
  68. {
  69. $iMinDuration = $iDuration;
  70. $sSLTName = $oSLT->GetName();
  71. }
  72. }
  73. if ($iMinDuration == PHP_INT_MAX)
  74. {
  75. $iDeadline = null;
  76. }
  77. else
  78. {
  79. // Store $sSLTName to keep track of which SLT has been used
  80. $iDeadline = $iMinDuration;
  81. }
  82. }
  83. return $iDeadline;
  84. }
  85. }
  86. /**
  87. * Compute the TTO of a ticket - null if the class 'SLT' does not exist
  88. */
  89. class ResponseTicketTTO extends ResponseTicketSLT implements iMetricComputer
  90. {
  91. public static function GetDescription()
  92. {
  93. return "Time to own a ticket";
  94. }
  95. public function ComputeMetric($oObject)
  96. {
  97. $iRes = $this->ComputeSLT($oObject, 'TTO');
  98. return $iRes;
  99. }
  100. }
  101. /**
  102. * Compute the TTR of a ticket - null if the class 'SLT' does not exist
  103. */
  104. class ResponseTicketTTR extends ResponseTicketSLT implements iMetricComputer
  105. {
  106. public static function GetDescription()
  107. {
  108. return "Time to resolve a ticket";
  109. }
  110. public function ComputeMetric($oObject)
  111. {
  112. $iRes = $this->ComputeSLT($oObject, 'TTR');
  113. return $iRes;
  114. }
  115. }
  116. class _Ticket extends cmdbAbstractObject
  117. {
  118. public function UpdateImpactedItems()
  119. {
  120. require_once(APPROOT.'core/displayablegraph.class.inc.php');
  121. $oContactsSet = $this->Get('contacts_list');
  122. $oCIsSet = $this->Get('functionalcis_list');
  123. $aCIsToImpactCode = array();
  124. $aSources = array();
  125. $aExcluded = array();
  126. $oCIsSet->Rewind();
  127. while ($oLink = $oCIsSet->Fetch())
  128. {
  129. $iKey = $oLink->Get('functionalci_id');
  130. $aCIsToImpactCode[$iKey] = $oLink->Get('impact_code');
  131. if ($oLink->Get('impact_code') == 'manual')
  132. {
  133. $oObj = MetaModel::GetObject('FunctionalCI', $iKey);
  134. $aSources[$iKey] = $oObj;
  135. }
  136. else if ($oLink->Get('impact_code') == 'not_impacted')
  137. {
  138. $oObj = MetaModel::GetObject('FunctionalCI', $iKey);
  139. $aExcluded[$iKey] = $oObj;
  140. }
  141. }
  142. $aContactsToRoleCode = array();
  143. $oContactsSet->Rewind();
  144. while ($oLink = $oContactsSet->Fetch())
  145. {
  146. $iKey = $oLink->Get('contact_id');
  147. $aContactsToRoleCode[$iKey] = $oLink->Get('role_code');
  148. if ($oLink->Get('role_code') == 'do_not_notify')
  149. {
  150. $oObj = MetaModel::GetObject('Contact', $iKey);
  151. $aExcluded[$iKey] = $oObj;
  152. }
  153. }
  154. $oNewCIsSet = DBObjectSet::FromScratch('lnkFunctionalCIToTicket');
  155. foreach($aCIsToImpactCode as $iKey => $sImpactCode)
  156. {
  157. if ($sImpactCode != 'computed')
  158. {
  159. $oNewLink = new lnkFunctionalCIToTicket();
  160. $oNewLink->Set('functionalci_id', $iKey);
  161. $oNewLink->Set('impact_code', $sImpactCode);
  162. $oNewCIsSet->AddObject($oNewLink);
  163. }
  164. }
  165. $oNewContactsSet = DBObjectSet::FromScratch('lnkContactToTicket');
  166. foreach($aContactsToRoleCode as $iKey => $sImpactCode)
  167. {
  168. if ($sImpactCode != 'computed')
  169. {
  170. $oNewLink = new lnkContactToTicket();
  171. $oNewLink->Set('contact_id', $iKey);
  172. $oNewLink->Set('role_code', $sImpactCode);
  173. $oNewContactsSet->AddObject($oNewLink);
  174. }
  175. }
  176. $oContactsSet = DBObjectSet::FromScratch('lnkContactToTicket');
  177. $sContextKey = 'itop-tickets/relation_context/'.get_class($this).'/impacts/down';
  178. $aContextDefs = DisplayableGraph::GetContextDefinitions($sContextKey, true, array('this' => $this));
  179. $aDefaultContexts = array();
  180. foreach($aContextDefs as $sKey => $aDefinition)
  181. {
  182. // Add the default context queries to the computation
  183. if (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes'))
  184. {
  185. $aDefaultContexts[] = $aDefinition['oql'];
  186. }
  187. }
  188. // Merge the directly impacted items with the "new" ones added by the "context" queries
  189. $aGraphObjects = array();
  190. $oRawGraph = MetaModel::GetRelatedObjectsDown('impacts', $aSources, 10, true /* bEnableRedundancy */, $aExcluded);
  191. $oIterator = new RelationTypeIterator($oRawGraph, 'Node');
  192. foreach ($oIterator as $oNode)
  193. {
  194. // Any object node reached AND different from a source will do
  195. if ( ($oNode instanceof RelationObjectNode) && ($oNode->GetProperty('is_reached')) && (!$oNode->GetProperty('source')) )
  196. {
  197. $oObj = $oNode->GetProperty('object');
  198. $iKey = $oObj->GetKey();
  199. $sRootClass = MetaModel::GetRootClass(get_class($oObj));
  200. $aGraphObjects[get_class($oObj).'::'.$iKey] = $oNode->GetProperty('object');
  201. }
  202. }
  203. if (count($aDefaultContexts) > 0)
  204. {
  205. $oAnnotatedGraph = MetaModel::GetRelatedObjectsDown('impacts', $aSources, 10, true /* bEnableRedundancy */, $aExcluded, $aDefaultContexts);
  206. $oIterator = new RelationTypeIterator($oAnnotatedGraph, 'Node');
  207. foreach ($oIterator as $oNode)
  208. {
  209. // Only pick the nodes which are NOT impacted by a context root cause, and merge them in the list
  210. if ( ($oNode instanceof RelationObjectNode) && ($oNode->GetProperty('is_reached')) && (!$oNode->GetProperty('source')) && ($oNode->GetProperty('context_root_causes', null) == null) )
  211. {
  212. $oObj = $oNode->GetProperty('object');
  213. $iKey = $oObj->GetKey();
  214. $sRootClass = MetaModel::GetRootClass(get_class($oObj));
  215. $aGraphObjects[get_class($oObj).'::'.$iKey] = $oNode->GetProperty('object');
  216. }
  217. }
  218. }
  219. foreach ($aGraphObjects as $oObj)
  220. {
  221. $iKey = $oObj->GetKey();
  222. $sRootClass = MetaModel::GetRootClass(get_class($oObj));
  223. switch ($sRootClass)
  224. {
  225. case 'FunctionalCI':
  226. // Only link FunctionalCIs which are not already linked to the ticket
  227. if (!array_key_exists($iKey, $aCIsToImpactCode) || ($aCIsToImpactCode[$iKey] != 'not_impacted'))
  228. {
  229. $oNewLink = new lnkFunctionalCIToTicket();
  230. $oNewLink->Set('functionalci_id', $iKey);
  231. $oNewLink->Set('impact_code', 'computed');
  232. $oNewCIsSet->AddObject($oNewLink);
  233. }
  234. break;
  235. case 'Contact':
  236. // Only link Contacts which are not already linked to the ticket
  237. if (!array_key_exists($iKey, $aContactsToRoleCode) || ($aCIsToImpactCode[$iKey] != 'do_not_notify'))
  238. {
  239. $oNewLink = new lnkContactToTicket();
  240. $oNewLink->Set('contact_id', $iKey);
  241. $oNewLink->Set('role_code', 'computed');
  242. $oNewContactsSet->AddObject($oNewLink);
  243. }
  244. break;
  245. }
  246. }
  247. $this->Set('functionalcis_list', $oNewCIsSet);
  248. $this->Set('contacts_list', $oNewContactsSet);
  249. }
  250. public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
  251. {
  252. parent::DisplayBareRelations($oPage, $bEditMode);
  253. // Display the impact analysis for tickets not in 'closed' or 'resolved' status... and not in edition
  254. if ((!$bEditMode) && (!in_array($this->Get('status'), array('resolved', 'closed'))))
  255. {
  256. $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/fraphael.js');
  257. $oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/jquery.contextMenu.css');
  258. $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.contextMenu.js');
  259. $oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/simple_graph.js');
  260. $oPage->AddAjaxTab(Dict::S('Ticket:ImpactAnalysis'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=ticket_impact&class='.get_class($this).'&id='.$this->GetKey(), true);
  261. }
  262. }
  263. }
  264. ?>