FOSSology  3.2.0rc1
Open Source License Compliance by Open Source Software
DeciderAgent.php
Go to the documentation of this file.
1 <?php
2 /*
3  Author: Daniele Fognini
4  Copyright (C) 2014-2019, Siemens AG
5 
6  This program is free software; you can redistribute it and/or
7  modify it under the terms of the GNU General Public License
8  version 2 as published by the Free Software Foundation.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc.,
17  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
54 namespace Fossology\Decider;
55 
68 
69 include_once(__DIR__ . "/version.php");
70 
75 class DeciderAgent extends Agent
76 {
77  const RULES_NOMOS_IN_MONK = 0x1;
78  const RULES_NOMOS_MONK_NINKA = 0x2;
79  const RULES_BULK_REUSE = 0x4;
80  const RULES_WIP_SCANNER_UPDATES = 0x8;
81  const RULES_OJO_NO_CONTRADICTION = 0x10;
82  const RULES_ALL = 0xf; // self::RULES_NOMOS_IN_MONK | self::RULES_NOMOS_MONK_NINKA | ... -> feature not available in php5.3
83 
87  private $activeRules;
91  private $uploadDao;
103  private $clearingDao;
107  private $highlightDao;
111  private $showJobsDao;
115  private $decisionTypes;
119  private $licenseMap = null;
123  private $licenseMapUsage = null;
124 
125  function __construct($licenseMapUsage=null)
126  {
127  parent::__construct(AGENT_DECIDER_NAME, AGENT_DECIDER_VERSION, AGENT_DECIDER_REV);
128 
129  $this->uploadDao = $this->container->get('dao.upload');
130  $this->clearingDao = $this->container->get('dao.clearing');
131  $this->highlightDao = $this->container->get('dao.highlight');
132  $this->showJobsDao = $this->container->get('dao.show_jobs');
133  $this->decisionTypes = $this->container->get('decision.types');
134  $this->clearingDecisionProcessor = $this->container->get('businessrules.clearing_decision_processor');
135  $this->agentLicenseEventProcessor = $this->container->get('businessrules.agent_license_event_processor');
136 
137  $this->licenseMapUsage = $licenseMapUsage;
138  $this->agentSpecifOptions = "r:";
139  }
140 
145  function processUploadId($uploadId)
146  {
147  $args = $this->args;
148  $this->activeRules = array_key_exists('r', $args) ? intval($args['r']) : self::RULES_ALL;
149  $this->licenseMap = new LicenseMap($this->dbManager, $this->groupId, $this->licenseMapUsage);
150 
151  if (array_key_exists("r", $args) && (($this->activeRules&self::RULES_BULK_REUSE)== self::RULES_BULK_REUSE)) {
152  $bulkReuser = new BulkReuser();
153  $bulkIds = $this->clearingDao->getPreviousBulkIds($uploadId, $this->groupId, $this->userId);
154  if (count($bulkIds) == 0) {
155  return true;
156  }
157  $jqId=0;
158  $minTime="4";
159  $maxTime="60";
160  foreach ($bulkIds as $bulkId) {
161  $jqId = $bulkReuser->rerunBulkAndDeciderOnUpload($uploadId, $this->groupId, $this->userId, $bulkId, $jqId);
162  $this->heartbeat(1);
163  if (!empty($jqId)) {
164  $jqIdRow = $this->showJobsDao->getDataForASingleJob($jqId);
165  while ($this->showJobsDao->getJobStatus($jqId)) {
166  $this->heartbeat(0);
167  $timeInSec = $this->showJobsDao->getEstimatedTime($jqIdRow['jq_job_fk'],'',0,0,1);
168  if ($timeInSec > $maxTime) {
169  sleep($maxTime);
170  } else if ($timeInSec < $minTime) {
171  sleep($minTime);
172  } else {
173  sleep($timeInSec);
174  }
175  }
176  }
177  }
178  }
179  $parentBounds = $this->uploadDao->getParentItemBounds($uploadId);
180  foreach ($this->uploadDao->getContainedItems($parentBounds) as $item) {
181  $process = $this->processItem($item);
182  $this->heartbeat($process);
183  }
184  return true;
185  }
186 
196  private function processItem(Item $item)
197  {
198  $itemTreeBounds = $item->getItemTreeBounds();
199 
200  $unMappedMatches = $this->agentLicenseEventProcessor->getLatestScannerDetectedMatches($itemTreeBounds);
201  $projectedScannerMatches = $this->remapByProjectedId($unMappedMatches);
202 
203  $lastDecision = $this->clearingDao->getRelevantClearingDecision($itemTreeBounds, $this->groupId);
204 
205  if (null!==$lastDecision && $lastDecision->getType()==DecisionTypes::IRRELEVANT) {
206  return 0;
207  }
208 
209  $currentEvents = $this->clearingDao->getRelevantClearingEvents($itemTreeBounds, $this->groupId);
210 
211  $markAsWip = false;
212  if (null !== $lastDecision && $projectedScannerMatches
213  && ($this->activeRules & self::RULES_WIP_SCANNER_UPDATES) == self::RULES_WIP_SCANNER_UPDATES) {
214  $licensesFromDecision = array();
215  foreach ($lastDecision->getClearingLicenses() as $clearingLicense) {
216  $licenseIdFromEvent = $this->licenseMap->getProjectedId($clearingLicense->getLicenseId());
217  $licensesFromDecision[$licenseIdFromEvent] = $licenseIdFromEvent;
218  }
219  $markAsWip = $this->existsUnhandledMatch($projectedScannerMatches,$licensesFromDecision);
220  }
221 
222  if (null !== $lastDecision && $markAsWip) {
223  $this->clearingDao->markDecisionAsWip($item->getId(), $this->userId, $this->groupId);
224  return 1;
225  }
226 
227  if (null!==$lastDecision || 0<count($currentEvents)) {
228  return 0;
229  }
230 
231  $haveDecided = false;
232 
233  if (($this->activeRules&self::RULES_OJO_NO_CONTRADICTION) == self::RULES_OJO_NO_CONTRADICTION) {
234  $haveDecided = $this->autodecideIfOjoMatchesNoContradiction($itemTreeBounds, $projectedScannerMatches);
235  }
236 
237  if (!$haveDecided && ($this->activeRules&self::RULES_NOMOS_IN_MONK) == self::RULES_NOMOS_IN_MONK) {
238  $haveDecided = $this->autodecideNomosMatchesInsideMonk($itemTreeBounds, $projectedScannerMatches);
239  }
240 
241  if (!$haveDecided && ($this->activeRules&self::RULES_NOMOS_MONK_NINKA)== self::RULES_NOMOS_MONK_NINKA) {
242  $haveDecided = $this->autodecideNomosMonkNinka($itemTreeBounds, $projectedScannerMatches);
243  }
244 
245  if (!$haveDecided && $markAsWip) {
246  $this->clearingDao->markDecisionAsWip($item->getId(), $this->userId, $this->groupId);
247  }
248 
249  return ($haveDecided||$markAsWip ? 1 : 0);
250  }
251 
258  private function existsUnhandledMatch($projectedScannerMatches, $licensesFromDecision)
259  {
260  foreach (array_keys($projectedScannerMatches) as $projectedLicenseId) {
261  if (!array_key_exists($projectedLicenseId, $licensesFromDecision)) {
262  return true;
263  }
264  }
265  return false;
266  }
267 
276  private function autodecideIfOjoMatchesNoContradiction(ItemTreeBounds $itemTreeBounds, $matches)
277  {
278  $licenseMatchExists = count($matches) > 0;
279  foreach ($matches as $licenseMatches) {
280  $licenseMatchExists = $licenseMatchExists && $this->areOtherScannerFindingsAndOJOAgreed($licenseMatches);
281  }
282 
283  if ($licenseMatchExists) {
284  $this->clearingDecisionProcessor->makeDecisionFromLastEvents($itemTreeBounds, $this->userId, $this->groupId, DecisionTypes::IDENTIFIED, $global=true);
285  }
286  return $licenseMatchExists;
287  }
288 
297  private function autodecideNomosMonkNinka(ItemTreeBounds $itemTreeBounds, $matches)
298  {
299  $canDecide = (count($matches)>0);
300 
301  foreach ($matches as $licenseMatches) {
302  if (!$canDecide) { // &= is not lazy
303  break;
304  }
305  $canDecide &= $this->areNomosMonkNinkaAgreed($licenseMatches);
306  }
307 
308  if ($canDecide) {
309  $this->clearingDecisionProcessor->makeDecisionFromLastEvents($itemTreeBounds, $this->userId, $this->groupId, DecisionTypes::IDENTIFIED, $global=true);
310  }
311  return $canDecide;
312  }
313 
322  private function autodecideNomosMatchesInsideMonk(ItemTreeBounds $itemTreeBounds, $matches)
323  {
324  $canDecide = (count($matches)>0);
325 
326  foreach ($matches as $licenseMatches) {
327  if (!$canDecide) { // &= is not lazy
328  break;
329  }
330  $canDecide &= $this->areNomosMatchesInsideAMonkMatch($licenseMatches);
331  }
332 
333  if ($canDecide) {
334  $this->clearingDecisionProcessor->makeDecisionFromLastEvents($itemTreeBounds, $this->userId, $this->groupId, DecisionTypes::IDENTIFIED, $global=true);
335  }
336  return $canDecide;
337  }
338 
345  protected function remapByProjectedId($matches)
346  {
347  $remapped = array();
348  foreach ($matches as $licenseId => $licenseMatches) {
349  $projectedId = $this->licenseMap->getProjectedId($licenseId);
350 
351  foreach ($licenseMatches as $agent => $agentMatches) {
352  $haveId = array_key_exists($projectedId, $remapped);
353  $haveAgent = $haveId && array_key_exists($agent, $remapped[$projectedId]);
354  if ($haveAgent) {
355  $remapped[$projectedId][$agent] = array_merge($remapped[$projectedId][$agent], $agentMatches);
356  } else {
357  $remapped[$projectedId][$agent] = $agentMatches;
358  }
359  }
360  }
361  return $remapped;
362  }
363 
370  private function isRegionIncluded($small, $big)
371  {
372  return ($big[0] >= 0) && ($small[0] >= $big[0]) && ($small[1] <= $big[1]);
373  }
374 
380  private function areNomosMatchesInsideAMonkMatch($licenseMatches)
381  {
382  if (!array_key_exists("nomos", $licenseMatches)) {
383  return false;
384  }
385  if (!array_key_exists("monk", $licenseMatches)) {
386  return false;
387  }
388 
389  foreach ($licenseMatches["nomos"] as $licenseMatch) {
390  $matchId = $licenseMatch->getLicenseFileId();
391  $nomosRegion = $this->highlightDao->getHighlightRegion($matchId);
392 
393  $found = false;
394  foreach ($licenseMatches["monk"] as $monkLicenseMatch) {
395  $monkRegion = $this->highlightDao->getHighlightRegion($monkLicenseMatch->getLicenseFileId());
396  if ($this->isRegionIncluded($nomosRegion, $monkRegion)) {
397  $found = true;
398  break;
399  }
400  }
401  if (!$found) {
402  return false;
403  }
404  }
405 
406  return true;
407  }
408 
414  protected function areNomosMonkNinkaAgreed($licenseMatches)
415  {
416  $scanners = array('nomos','monk','ninka');
417  $vote = array();
418  foreach ($scanners as $scanner) {
419  if (!array_key_exists($scanner, $licenseMatches)) {
420  return false;
421  }
422  foreach ($licenseMatches[$scanner] as $licenseMatch) {
423  $licId = $licenseMatch->getLicenseId();
424  $vote[$licId][$scanner] = true;
425  }
426  }
427 
428  foreach ($vote as $licId=>$voters) {
429  if (count($voters) != 3) {
430  return false;
431  }
432  }
433  return true;
434  }
435 
442  protected function getLicenseIdsOfMatchesForScanner($scanner, $licenseMatches)
443  {
444  if (array_key_exists($scanner, $licenseMatches) === true) {
445  return array_map(
446  function ($match) {
447  return $match->getLicenseId();
448  }, $licenseMatches[$scanner]);
449  }
450  return [];
451  }
452 
458  protected function areOtherScannerFindingsAndOJOAgreed($licenseMatches)
459  {
460  $findingsByOjo = $this->getLicenseIdsOfMatchesForScanner('ojo', $licenseMatches);
461  if (count($findingsByOjo) == 0) {
462  // nothing to do
463  return false;
464  }
465 
466  $findingsByOtherScanner = $this->getLicenseIdsOfMatchesForScanner('nomos', $licenseMatches);
467  if (count($findingsByOtherScanner) == 0) {
468  // nothing found by other scanner, so no contradiction
469  return true;
470  }
471  foreach ($findingsByOtherScanner as $findingsByScanner) {
472  if (in_array($findingsByScanner, $findingsByOjo) === false) {
473  // contradiction found
474  return false;
475  }
476  }
477  return true;
478  }
479 }
heartbeat($newProcessed)
Send hear beat to the scheduler.
Definition: Agent.php:214
Structure of an Agent with all required parameters.
Definition: Agent.php:51
Namespace for decider agent.
Definition: BulkReuser.php:19
Agent to decide license findings in an upload.
Wrapper class for license map.
Definition: LicenseMap.php:29
areNomosMonkNinkaAgreed($licenseMatches)
Check if findings by all agents are same or not.
processItem(Item $item)
Given an item, check with the $activeRules and apply rules to it.
areNomosMatchesInsideAMonkMatch($licenseMatches)
Check if matches by nomos are inside monk findings.
autodecideNomosMonkNinka(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches which are in nomos, monk and ninka findings.
existsUnhandledMatch($projectedScannerMatches, $licensesFromDecision)
Check if matches contains unhandled match.
getLicenseIdsOfMatchesForScanner($scanner, $licenseMatches)
extracts the matches corresponding to a scanner from a $licenseMatches structure
areOtherScannerFindingsAndOJOAgreed($licenseMatches)
Check if the finding by only contains one single license and that no other scanner (nomos) has produc...
autodecideNomosMatchesInsideMonk(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches by nomos which are in monk findings.
autodecideIfOjoMatchesNoContradiction(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches which are in nomos, monk and OJO findings.
isRegionIncluded($small, $big)
Check if the small highlight region is inside big one.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:28
processUploadId($uploadId)
Given an upload ID, process the items in it.
remapByProjectedId($matches)
Given a set of matches, remap according to project id instead of license id.
Prepares bulk licenses for an upload and run DeciderJob on it.
Definition: BulkReuser.php:33