FOSSology  4.5.0-rc1
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  SPDX-FileCopyrightText: © 2014-2019 Siemens AG
5  SPDX-FileCopyrightText: © 2021 Orange by Piotr Pszczola <piotr.pszczola@orange.com>
6  SPDX-FileCopyrightText: © 2021 Kaushlendra Pratap <kaushlendrapratap.9837@gmail.com>
7 
8  SPDX-License-Identifier: GPL-2.0-only
9 */
49 namespace Fossology\Decider;
50 
69 use Symfony\Component\Process\Process;
70 
71 include_once(__DIR__ . "/version.php");
72 
77 class DeciderAgent extends Agent
78 {
79  const RULES_NOMOS_IN_MONK = 0x1;
80  const RULES_NOMOS_MONK_NINKA = 0x2;
81  const RULES_BULK_REUSE = 0x4;
82  const RULES_WIP_SCANNER_UPDATES = 0x8;
83  const RULES_OJO_NO_CONTRADICTION = 0x10;
84  const RULES_COPYRIGHT_FALSE_POSITIVE = 0x20;
85  const RULES_COPYRIGHT_FALSE_POSITIVE_CLUTTER = 0x40;
86  const RULES_LICENSE_TYPE_CONCLUSION = 0x80;
87  const RULES_ALL = self::RULES_NOMOS_IN_MONK | self::RULES_NOMOS_MONK_NINKA |
88  self::RULES_BULK_REUSE | self::RULES_WIP_SCANNER_UPDATES |
89  self::RULES_OJO_NO_CONTRADICTION | self::RULES_LICENSE_TYPE_CONCLUSION;
90 
94  private $activeRules;
98  private $licenseType;
102  private $uploadDao;
114  private $clearingDao;
118  private $highlightDao;
122  private $showJobsDao;
126  private $decisionTypes;
130  private $licenseMap = null;
134  private $licenseMapUsage = null;
135 
139  private $copyrightDao;
140 
145 
149  private $licenseDao;
150 
151  function __construct($licenseMapUsage=null)
152  {
153  parent::__construct(AGENT_DECIDER_NAME, AGENT_DECIDER_VERSION, AGENT_DECIDER_REV);
154 
155  $this->uploadDao = $this->container->get('dao.upload');
156  $this->clearingDao = $this->container->get('dao.clearing');
157  $this->highlightDao = $this->container->get('dao.highlight');
158  $this->showJobsDao = $this->container->get('dao.show_jobs');
159  $this->decisionTypes = $this->container->get('decision.types');
160  $this->clearingDecisionProcessor = $this->container->get('businessrules.clearing_decision_processor');
161  $this->agentLicenseEventProcessor = $this->container->get('businessrules.agent_license_event_processor');
162  $this->copyrightDao = $this->container->get('dao.copyright');
163  $this->compatibilityDao = $this->container->get('dao.compatibility');
164  $this->licenseDao = $this->container->get('dao.license');
165  $this->licenseMapUsage = $licenseMapUsage;
166  $this->agentSpecifOptions = "r:t:";
167  }
168 
173  function processUploadId($uploadId)
174  {
175  $args = $this->args;
176  $this->activeRules = array_key_exists('r', $args) ? intval($args['r']) : self::RULES_ALL;
177  $this->licenseType = array_key_exists('t', $args) ?
178  $this->getLicenseType(str_replace(["'", '"'], "", $args['t'])) : "";
179  $this->licenseMap = new LicenseMap($this->dbManager, $this->groupId, $this->licenseMapUsage);
180 
181  if (array_key_exists("r", $args) && (($this->activeRules&self::RULES_COPYRIGHT_FALSE_POSITIVE)== self::RULES_COPYRIGHT_FALSE_POSITIVE)) {
182  $this->getCopyrightsToDisableFalsePositivesClutter($uploadId, false);
183  }
184  if (array_key_exists("r", $args) && (($this->activeRules&self::RULES_COPYRIGHT_FALSE_POSITIVE_CLUTTER)== self::RULES_COPYRIGHT_FALSE_POSITIVE_CLUTTER)) {
185  $this->getCopyrightsToDisableFalsePositivesClutter($uploadId, true);
186  }
187  if (array_key_exists("r", $args) && (($this->activeRules&self::RULES_BULK_REUSE)== self::RULES_BULK_REUSE)) {
188  $bulkReuser = new BulkReuser();
189  $bulkIds = $this->clearingDao->getPreviousBulkIds($uploadId, $this->groupId, $this->userId);
190  if (count($bulkIds) == 0) {
191  return true;
192  }
193  $jqId=0;
194  $minTime="4";
195  $maxTime="60";
196  foreach ($bulkIds as $bulkId) {
197  $jqId = $bulkReuser->rerunBulkAndDeciderOnUpload($uploadId, $this->groupId, $this->userId, $bulkId, $jqId);
198  $this->heartbeat(1);
199  if (!empty($jqId)) {
200  $jqIdRow = $this->showJobsDao->getDataForASingleJob($jqId);
201  while ($this->showJobsDao->getJobStatus($jqId)) {
202  $this->heartbeat(0);
203  $timeInSec = $this->showJobsDao->getEstimatedTime($jqIdRow['jq_job_fk'],'',0,0,1);
204  if ($timeInSec > $maxTime) {
205  sleep($maxTime);
206  } else if ($timeInSec < $minTime) {
207  sleep($minTime);
208  } else {
209  sleep($timeInSec);
210  }
211  }
212  }
213  }
214  }
215  $parentBounds = $this->uploadDao->getParentItemBounds($uploadId);
216  foreach ($this->uploadDao->getContainedItems($parentBounds) as $item) {
217  $process = $this->processItem($item);
218  $this->heartbeat($process);
219  }
220  return true;
221  }
222 
232  private function processItem(Item $item)
233  {
234  $itemTreeBounds = $item->getItemTreeBounds();
235 
236  $unMappedMatches = $this->agentLicenseEventProcessor->getLatestScannerDetectedMatches($itemTreeBounds);
237  $projectedScannerMatches = $this->remapByProjectedId($unMappedMatches);
238 
239  $lastDecision = $this->clearingDao->getRelevantClearingDecision($itemTreeBounds, $this->groupId);
240 
241  if (null!==$lastDecision && $lastDecision->getType()==DecisionTypes::IRRELEVANT) {
242  return 0;
243  }
244 
245  $currentEvents = $this->clearingDao->getRelevantClearingEvents($itemTreeBounds, $this->groupId);
246 
247  $markAsWip = false;
248  if (null !== $lastDecision && $projectedScannerMatches
249  && ($this->activeRules & self::RULES_WIP_SCANNER_UPDATES) == self::RULES_WIP_SCANNER_UPDATES) {
250  $licensesFromDecision = array();
251  foreach ($lastDecision->getClearingLicenses() as $clearingLicense) {
252  $licenseIdFromEvent = $this->licenseMap->getProjectedId($clearingLicense->getLicenseId());
253  $licensesFromDecision[$licenseIdFromEvent] = $licenseIdFromEvent;
254  }
255  $markAsWip = $this->existsUnhandledMatch($projectedScannerMatches,$licensesFromDecision);
256  }
257 
258  if (null !== $lastDecision && $markAsWip) {
259  $this->clearingDao->markDecisionAsWip($item->getId(), $this->userId, $this->groupId);
260  return 1;
261  }
262 
263  if (null!==$lastDecision || 0<count($currentEvents)) {
264  return 0;
265  }
266 
267  $haveDecided = false;
268 
269  if (($this->activeRules&self::RULES_OJO_NO_CONTRADICTION) == self::RULES_OJO_NO_CONTRADICTION) {
270  $haveDecided = $this->autodecideIfOjoMatchesNoContradiction($itemTreeBounds, $projectedScannerMatches);
271  }
272 
273  if (!$haveDecided && ($this->activeRules&self::RULES_OJO_NO_CONTRADICTION) == self::RULES_OJO_NO_CONTRADICTION) {
274  $haveDecided = $this->autodecideIfResoMatchesNoContradiction($itemTreeBounds, $projectedScannerMatches);
275  }
276 
277  if (!$haveDecided && ($this->activeRules&self::RULES_NOMOS_IN_MONK) == self::RULES_NOMOS_IN_MONK) {
278  $haveDecided = $this->autodecideNomosMatchesInsideMonk($itemTreeBounds, $projectedScannerMatches);
279  }
280 
281  if (!$haveDecided && ($this->activeRules&self::RULES_NOMOS_MONK_NINKA)== self::RULES_NOMOS_MONK_NINKA) {
282  $haveDecided = $this->autodecideNomosMonkNinka($itemTreeBounds, $projectedScannerMatches);
283  }
284 
285  if (!$haveDecided && ($this->activeRules &
286  self::RULES_LICENSE_TYPE_CONCLUSION) ==
287  self::RULES_LICENSE_TYPE_CONCLUSION) {
288  $haveDecided = $this->autodecideLicenseType($itemTreeBounds,
289  $projectedScannerMatches);
290  }
291 
292  if (!$haveDecided && $markAsWip) {
293  $this->clearingDao->markDecisionAsWip($item->getId(), $this->userId, $this->groupId);
294  }
295 
296  return ($haveDecided||$markAsWip ? 1 : 0);
297  }
298 
305  private function existsUnhandledMatch($projectedScannerMatches, $licensesFromDecision)
306  {
307  foreach (array_keys($projectedScannerMatches) as $projectedLicenseId) {
308  if (!array_key_exists($projectedLicenseId, $licensesFromDecision)) {
309  return true;
310  }
311  }
312  return false;
313  }
314 
323  private function autodecideIfOjoMatchesNoContradiction(ItemTreeBounds $itemTreeBounds, $matches)
324  {
325  $licenseMatchExists = count($matches) > 0;
326  foreach ($matches as $licenseMatches) {
327  $licenseMatchExists = $licenseMatchExists && $this->areOtherScannerFindingsAndOJOAgreed($licenseMatches);
328  }
329 
330  if ($licenseMatchExists) {
331  try {
332  $this->clearingDecisionProcessor->makeDecisionFromLastEvents(
333  $itemTreeBounds, $this->userId, $this->groupId,
334  DecisionTypes::IDENTIFIED, false);
335  } catch (\Exception $e) {
336  echo "Can not auto decide as file '" .
337  $itemTreeBounds->getItemId() . "' contains candidate license.\n";
338  }
339  }
340  return $licenseMatchExists;
341  }
342 
351  private function autodecideIfResoMatchesNoContradiction(ItemTreeBounds $itemTreeBounds, $matches)
352  {
353  $licenseMatchExists = count($matches) > 0;
354  foreach ($matches as $licenseMatches) {
355  $licenseMatchExists = $licenseMatchExists && $this->areOtherScannerFindingsAndRESOAgreed($licenseMatches);
356  }
357 
358  if ($licenseMatchExists) {
359  try {
360  $this->clearingDecisionProcessor->makeDecisionFromLastEvents(
361  $itemTreeBounds, $this->userId, $this->groupId,
362  DecisionTypes::IDENTIFIED, false);
363  } catch (\Exception $e) {
364  echo "Can not auto decide as file '" .
365  $itemTreeBounds->getItemId() . "' contains candidate license.\n";
366  }
367  }
368  return $licenseMatchExists;
369  }
370 
379  private function autodecideNomosMonkNinka(ItemTreeBounds $itemTreeBounds, $matches)
380  {
381  $canDecide = (count($matches)>0);
382 
383  foreach ($matches as $licenseMatches) {
384  if (!$canDecide) { // &= is not lazy
385  break;
386  }
387  $canDecide &= $this->areNomosMonkNinkaAgreed($licenseMatches);
388  }
389 
390  if ($canDecide) {
391  $this->clearingDecisionProcessor->makeDecisionFromLastEvents($itemTreeBounds, $this->userId, $this->groupId, DecisionTypes::IDENTIFIED, $global=true);
392  }
393  return $canDecide;
394  }
395 
404  private function autodecideNomosMatchesInsideMonk(ItemTreeBounds $itemTreeBounds, $matches)
405  {
406  $canDecide = (count($matches)>0);
407 
408  foreach ($matches as $licenseMatches) {
409  if (!$canDecide) { // &= is not lazy
410  break;
411  }
412  $canDecide &= $this->areNomosMatchesInsideAMonkMatch($licenseMatches);
413  }
414 
415  if ($canDecide) {
416  $this->clearingDecisionProcessor->makeDecisionFromLastEvents($itemTreeBounds, $this->userId, $this->groupId, DecisionTypes::IDENTIFIED, $global=true);
417  }
418  return $canDecide;
419  }
420 
430  private function autodecideLicenseType(ItemTreeBounds $itemTreeBounds,
431  $matches)
432  {
433  $canDecide = $this->noLicenseConflict($itemTreeBounds, $matches);
434  if ($canDecide) {
435  $canDecide &= $this->allLicenseInType($matches);
436  }
437 
438  if ($canDecide) {
439  $this->clearingDecisionProcessor
440  ->makeDecisionFromLastEvents($itemTreeBounds, $this->userId,
441  $this->groupId, DecisionTypes::IDENTIFIED, DecisionScopes::ITEM, [], true);
442  }
443  return $canDecide;
444  }
445 
452  protected function remapByProjectedId($matches)
453  {
454  $remapped = array();
455  foreach ($matches as $licenseId => $licenseMatches) {
456  $projectedId = $this->licenseMap->getProjectedId($licenseId);
457 
458  foreach ($licenseMatches as $agent => $agentMatches) {
459  $haveId = array_key_exists($projectedId, $remapped);
460  $haveAgent = $haveId && array_key_exists($agent, $remapped[$projectedId]);
461  if ($haveAgent) {
462  $remapped[$projectedId][$agent] = array_merge($remapped[$projectedId][$agent], $agentMatches);
463  } else {
464  $remapped[$projectedId][$agent] = $agentMatches;
465  }
466  }
467  }
468  return $remapped;
469  }
470 
477  private function isRegionIncluded($small, $big)
478  {
479  return ($big[0] >= 0) && ($small[0] >= $big[0]) && ($small[1] <= $big[1]);
480  }
481 
487  private function areNomosMatchesInsideAMonkMatch($licenseMatches)
488  {
489  if (!array_key_exists("nomos", $licenseMatches)) {
490  return false;
491  }
492  if (!array_key_exists("monk", $licenseMatches)) {
493  return false;
494  }
495 
496  foreach ($licenseMatches["nomos"] as $licenseMatch) {
497  $matchId = $licenseMatch->getLicenseFileId();
498  $nomosRegion = $this->highlightDao->getHighlightRegion($matchId);
499 
500  $found = false;
501  foreach ($licenseMatches["monk"] as $monkLicenseMatch) {
502  $monkRegion = $this->highlightDao->getHighlightRegion($monkLicenseMatch->getLicenseFileId());
503  if ($this->isRegionIncluded($nomosRegion, $monkRegion)) {
504  $found = true;
505  break;
506  }
507  }
508  if (!$found) {
509  return false;
510  }
511  }
512 
513  return true;
514  }
515 
521  protected function areNomosMonkNinkaAgreed($licenseMatches)
522  {
523  $scanners = array('nomos','monk','ninka');
524  $vote = array();
525  foreach ($scanners as $scanner) {
526  if (!array_key_exists($scanner, $licenseMatches)) {
527  return false;
528  }
529  foreach ($licenseMatches[$scanner] as $licenseMatch) {
530  $licId = $licenseMatch->getLicenseId();
531  $vote[$licId][$scanner] = true;
532  }
533  }
534 
535  foreach ($vote as $licId=>$voters) {
536  if (count($voters) != 3) {
537  return false;
538  }
539  }
540  return true;
541  }
542 
549  protected function getLicenseIdsOfMatchesForScanner($scanner, $licenseMatches)
550  {
551  if (array_key_exists($scanner, $licenseMatches) === true) {
552  return array_map(
553  function ($match) {
554  return $match->getLicenseId();
555  }, $licenseMatches[$scanner]);
556  }
557  return [];
558  }
559 
565  protected function areOtherScannerFindingsAndOJOAgreed($licenseMatches)
566  {
567  $findingsByOjo = $this->getLicenseIdsOfMatchesForScanner('ojo', $licenseMatches);
568  if (count($findingsByOjo) == 0) {
569  // nothing to do
570  return false;
571  }
572 
573  $findingsByOtherScanner = $this->getLicenseIdsOfMatchesForScanner('nomos', $licenseMatches);
574  if (count($findingsByOtherScanner) == 0) {
575  // nothing found by other scanner, so no contradiction
576  return true;
577  }
578  foreach ($findingsByOtherScanner as $findingsByScanner) {
579  if (in_array($findingsByScanner, $findingsByOjo) === false) {
580  // contradiction found
581  return false;
582  }
583  }
584  return true;
585  }
586 
592  protected function areOtherScannerFindingsAndRESOAgreed($licenseMatches)
593  {
594  $findingsByReso = $this->getLicenseIdsOfMatchesForScanner('reso', $licenseMatches);
595  if (count($findingsByReso) == 0) {
596  // nothing to do
597  return false;
598  }
599 
600  $findingsByOtherScanner = $this->getLicenseIdsOfMatchesForScanner('nomos', $licenseMatches);
601  if (count($findingsByOtherScanner) == 0) {
602  // nothing found by other scanner, so no contradiction
603  return true;
604  }
605  foreach ($findingsByOtherScanner as $findingsByScanner) {
606  if (in_array($findingsByScanner, $findingsByReso) === false) {
607  // contradiction found
608  return false;
609  }
610  }
611  return true;
612  }
613 
621  $clutter_flag): void
622  {
623  if (empty($uploadId)) {
624  return;
625  }
626  $agentName = 'copyright';
627  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
628  $scanJobProxy = new ScanJobProxy($GLOBALS['container']->get('dao.agent'), $uploadId);
629  $scanJobProxy->createAgentStatus(array($agentName));
630  $selectedScanners = $scanJobProxy->getLatestSuccessfulAgentIds();
631  if (!array_key_exists($agentName, $selectedScanners)) {
632  return;
633  }
634  $latestXpAgentId = $selectedScanners[$agentName];
635  $extrawhere = ' agent_fk='.$latestXpAgentId;
636  $allCopyrights = $this->copyrightDao->getScannerEntries('copyright',
637  $uploadTreeTableName, $uploadId, null, $extrawhere);
638 
639  $copyrightJSON = json_encode($allCopyrights);
640  $tmpFile = tmpfile();
641  $tmpFilePath = stream_get_meta_data($tmpFile)['uri'];
642  fwrite($tmpFile, $copyrightJSON);
643  $deactivatedCopyrightData = $this->callCopyrightDeactivationClutterRemovalScript($tmpFilePath, $clutter_flag);
644  if (empty($deactivatedCopyrightData)) {
645  fclose($tmpFile);
646  return;
647  }
648  $deactivatedCopyrights = json_decode($deactivatedCopyrightData, true);
649  foreach ($deactivatedCopyrights as $deactivatedCopyright) {
650  $item = $deactivatedCopyright['uploadtree_pk'];
651  $itemTreeBounds = $this->uploadDao->getItemTreeBounds($item, $uploadTreeTableName);
652  $hash = $deactivatedCopyright['hash'];
653  $content = $deactivatedCopyright['content'];
654  $cpTable = 'copyright';
655  if ($deactivatedCopyright['is_copyright'] == "t") {
656  $action = '';
657  if (array_key_exists('decluttered_content', $deactivatedCopyright) &&
658  !empty($deactivatedCopyright['decluttered_content'])) {
659  $content = $deactivatedCopyright['decluttered_content'];
660  } else {
661  // No text update. Nothing to do.
662  $this->heartbeat(1);
663  continue;
664  }
665  } else {
666  $action = 'delete';
667  }
668  $this->copyrightDao->updateTable($itemTreeBounds, $hash, $content, $this->userId, $cpTable, $action);
669  $this->heartbeat(1);
670  }
671  fclose($tmpFile);
672  }
673 
680  private function callCopyrightDeactivationClutterRemovalScript($tmpFilePath,
681  $clutter_flag): string
682  {
683  $script = "copyrightDeactivationClutterRemovalScript.py";
684  $args = ["python3", __DIR__ . "/$script", "--file", $tmpFilePath];
685  if ($clutter_flag) {
686  $args[] = "--clutter";
687  }
688 
689  $sleepTime = 5;
690  $maxSleepTime = 25;
691 
692  $process = new Process($args);
693  $process->setTimeout(null); // Remove timeout to run indefinitely.
694  $process->setEnv(set_python_path());
695  $process->start();
696 
697  do {
698  $this->heartbeat(0);
699  sleep($sleepTime);
700  if ($sleepTime < $maxSleepTime) {
701  $sleepTime += 5;
702  }
703  } while ($process->isRunning());
704 
705  echo $process->getErrorOutput();
706 
707  return $process->getOutput();
708  }
709 
715  private function getLicenseType($licenseType)
716  {
717  global $SysConf;
718  $licenseTypes = array_map('trim', explode(',',
719  $SysConf['SYSCONFIG']['LicenseTypes']));
720  if (in_array($licenseType, $licenseTypes)) {
721  return $licenseType;
722  }
723  return "";
724  }
725 
733  protected function noLicenseConflict($itemTreeBounds, $licenseMatches)
734  {
735  $shortnames = [];
736  foreach ($licenseMatches as $agentMatch) {
737  foreach ($agentMatch as $agentLicenseMatches) {
738  foreach ($agentLicenseMatches as $licenseMatch) {
739  $shortnames[$licenseMatch->getLicenseId()] = $licenseMatch
740  ->getLicenseRef()->getShortName();
741  }
742  }
743  }
744  foreach ($shortnames as $shortname) {
745  try {
746  if (! $this->compatibilityDao->getCompatibilityForFile($itemTreeBounds,
747  $shortname)) {
748  return false;
749  }
750  } catch (InvalidAgentStageException $_) {
751  return false;
752  }
753  }
754  return true;
755  }
756 
762  protected function allLicenseInType($licenseMatches)
763  {
764  $licenseTypes = [];
765  foreach ($licenseMatches as $agentMatch) {
766  foreach ($agentMatch as $agentLicenseMatches) {
767  foreach ($agentLicenseMatches as $licenseMatch) {
768  if (!array_key_exists($licenseMatch->getLicenseId(), $licenseTypes)) {
769  $licenseTypes[$licenseMatch->getLicenseId()] = $this->licenseDao
770  ->getLicenseType($licenseMatch->getLicenseId());
771  }
772  }
773  }
774  }
775  if (! in_array($this->licenseType, $licenseTypes)) {
776  return false;
777  }
778  if (! empty(array_diff($licenseTypes, [$this->licenseType]))) {
779  return false;
780  }
781  return true;
782  }
783 }
Prepares bulk licenses for an upload and run DeciderJob on it.
Definition: BulkReuser.php:23
Agent to decide license findings in an upload.
existsUnhandledMatch($projectedScannerMatches, $licensesFromDecision)
Check if matches contains unhandled match.
areOtherScannerFindingsAndRESOAgreed($licenseMatches)
Check if the finding by only contains one single license and that no other scanner (nomos) has produc...
autodecideIfResoMatchesNoContradiction(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches which are in nomos, monk, OJO and Reso findings.
processItem(Item $item)
Given an item, check with the $activeRules and apply rules to it.
areOtherScannerFindingsAndOJOAgreed($licenseMatches)
Check if the finding by only contains one single license and that no other scanner (nomos) has produc...
callCopyrightDeactivationClutterRemovalScript($tmpFilePath, $clutter_flag)
autodecideNomosMatchesInsideMonk(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches by nomos which are in monk findings.
noLicenseConflict($itemTreeBounds, $licenseMatches)
Check if findings by all agents are same or not.
autodecideNomosMonkNinka(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches which are in nomos, monk and ninka findings.
processUploadId($uploadId)
Given an upload ID, process the items in it.
allLicenseInType($licenseMatches)
Check if findings by all agents are same or not.
autodecideLicenseType(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches where there is no license conflict.
getLicenseIdsOfMatchesForScanner($scanner, $licenseMatches)
extracts the matches corresponding to a scanner from a $licenseMatches structure
isRegionIncluded($small, $big)
Check if the small highlight region is inside big one.
autodecideIfOjoMatchesNoContradiction(ItemTreeBounds $itemTreeBounds, $matches)
Auto decide matches which are in nomos, monk and OJO findings.
areNomosMatchesInsideAMonkMatch($licenseMatches)
Check if matches by nomos are inside monk findings.
getCopyrightsToDisableFalsePositivesClutter($uploadId, $clutter_flag)
remapByProjectedId($matches)
Given a set of matches, remap according to project id instead of license id.
areNomosMonkNinkaAgreed($licenseMatches)
Check if findings by all agents are same or not.
Structure of an Agent with all required parameters.
Definition: Agent.php:41
heartbeat($newProcessed)
Send hear beat to the scheduler.
Definition: Agent.php:203
Utility functions to process ClearingDecision.
Wrapper class for license map.
Definition: LicenseMap.php:19
set_python_path()
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
Namespace for decider agent.
Definition: BulkReuser.php:8
$GLOBALS['xyyzzzDeciderJob']