FOSSology  4.4.0
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(trim($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 
435  if ($canDecide) {
436  $canDecide &= $this->allLicenseInType($matches);
437  }
438 
439  if ($canDecide) {
440  $this->clearingDecisionProcessor
441  ->makeDecisionFromLastEvents($itemTreeBounds, $this->userId,
442  $this->groupId, DecisionTypes::IDENTIFIED, DecisionScopes::ITEM);
443  }
444  return $canDecide;
445  }
446 
453  protected function remapByProjectedId($matches)
454  {
455  $remapped = array();
456  foreach ($matches as $licenseId => $licenseMatches) {
457  $projectedId = $this->licenseMap->getProjectedId($licenseId);
458 
459  foreach ($licenseMatches as $agent => $agentMatches) {
460  $haveId = array_key_exists($projectedId, $remapped);
461  $haveAgent = $haveId && array_key_exists($agent, $remapped[$projectedId]);
462  if ($haveAgent) {
463  $remapped[$projectedId][$agent] = array_merge($remapped[$projectedId][$agent], $agentMatches);
464  } else {
465  $remapped[$projectedId][$agent] = $agentMatches;
466  }
467  }
468  }
469  return $remapped;
470  }
471 
478  private function isRegionIncluded($small, $big)
479  {
480  return ($big[0] >= 0) && ($small[0] >= $big[0]) && ($small[1] <= $big[1]);
481  }
482 
488  private function areNomosMatchesInsideAMonkMatch($licenseMatches)
489  {
490  if (!array_key_exists("nomos", $licenseMatches)) {
491  return false;
492  }
493  if (!array_key_exists("monk", $licenseMatches)) {
494  return false;
495  }
496 
497  foreach ($licenseMatches["nomos"] as $licenseMatch) {
498  $matchId = $licenseMatch->getLicenseFileId();
499  $nomosRegion = $this->highlightDao->getHighlightRegion($matchId);
500 
501  $found = false;
502  foreach ($licenseMatches["monk"] as $monkLicenseMatch) {
503  $monkRegion = $this->highlightDao->getHighlightRegion($monkLicenseMatch->getLicenseFileId());
504  if ($this->isRegionIncluded($nomosRegion, $monkRegion)) {
505  $found = true;
506  break;
507  }
508  }
509  if (!$found) {
510  return false;
511  }
512  }
513 
514  return true;
515  }
516 
522  protected function areNomosMonkNinkaAgreed($licenseMatches)
523  {
524  $scanners = array('nomos','monk','ninka');
525  $vote = array();
526  foreach ($scanners as $scanner) {
527  if (!array_key_exists($scanner, $licenseMatches)) {
528  return false;
529  }
530  foreach ($licenseMatches[$scanner] as $licenseMatch) {
531  $licId = $licenseMatch->getLicenseId();
532  $vote[$licId][$scanner] = true;
533  }
534  }
535 
536  foreach ($vote as $licId=>$voters) {
537  if (count($voters) != 3) {
538  return false;
539  }
540  }
541  return true;
542  }
543 
550  protected function getLicenseIdsOfMatchesForScanner($scanner, $licenseMatches)
551  {
552  if (array_key_exists($scanner, $licenseMatches) === true) {
553  return array_map(
554  function ($match) {
555  return $match->getLicenseId();
556  }, $licenseMatches[$scanner]);
557  }
558  return [];
559  }
560 
566  protected function areOtherScannerFindingsAndOJOAgreed($licenseMatches)
567  {
568  $findingsByOjo = $this->getLicenseIdsOfMatchesForScanner('ojo', $licenseMatches);
569  if (count($findingsByOjo) == 0) {
570  // nothing to do
571  return false;
572  }
573 
574  $findingsByOtherScanner = $this->getLicenseIdsOfMatchesForScanner('nomos', $licenseMatches);
575  if (count($findingsByOtherScanner) == 0) {
576  // nothing found by other scanner, so no contradiction
577  return true;
578  }
579  foreach ($findingsByOtherScanner as $findingsByScanner) {
580  if (in_array($findingsByScanner, $findingsByOjo) === false) {
581  // contradiction found
582  return false;
583  }
584  }
585  return true;
586  }
587 
593  protected function areOtherScannerFindingsAndRESOAgreed($licenseMatches)
594  {
595  $findingsByReso = $this->getLicenseIdsOfMatchesForScanner('reso', $licenseMatches);
596  if (count($findingsByReso) == 0) {
597  // nothing to do
598  return false;
599  }
600 
601  $findingsByOtherScanner = $this->getLicenseIdsOfMatchesForScanner('nomos', $licenseMatches);
602  if (count($findingsByOtherScanner) == 0) {
603  // nothing found by other scanner, so no contradiction
604  return true;
605  }
606  foreach ($findingsByOtherScanner as $findingsByScanner) {
607  if (in_array($findingsByScanner, $findingsByReso) === false) {
608  // contradiction found
609  return false;
610  }
611  }
612  return true;
613  }
614 
622  $clutter_flag): void
623  {
624  if (empty($uploadId)) {
625  return;
626  }
627  $agentName = 'copyright';
628  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
629  $scanJobProxy = new ScanJobProxy($GLOBALS['container']->get('dao.agent'), $uploadId);
630  $scanJobProxy->createAgentStatus(array($agentName));
631  $selectedScanners = $scanJobProxy->getLatestSuccessfulAgentIds();
632  if (!array_key_exists($agentName, $selectedScanners)) {
633  return;
634  }
635  $latestXpAgentId = $selectedScanners[$agentName];
636  $extrawhere = ' agent_fk='.$latestXpAgentId;
637  $allCopyrights = $this->copyrightDao->getScannerEntries('copyright',
638  $uploadTreeTableName, $uploadId, null, $extrawhere);
639 
640  $copyrightJSON = json_encode($allCopyrights);
641  $tmpFile = tmpfile();
642  $tmpFilePath = stream_get_meta_data($tmpFile)['uri'];
643  fwrite($tmpFile, $copyrightJSON);
644  $deactivatedCopyrightData = $this->callCopyrightDeactivationClutterRemovalScript($tmpFilePath, $clutter_flag);
645  if (empty($deactivatedCopyrightData)) {
646  fclose($tmpFile);
647  return;
648  }
649  $deactivatedCopyrights = json_decode($deactivatedCopyrightData, true);
650  foreach ($deactivatedCopyrights as $deactivatedCopyright) {
651  $item = $deactivatedCopyright['uploadtree_pk'];
652  $itemTreeBounds = $this->uploadDao->getItemTreeBounds($item, $uploadTreeTableName);
653  $hash = $deactivatedCopyright['hash'];
654  $content = $deactivatedCopyright['content'];
655  $cpTable = 'copyright';
656  if ($deactivatedCopyright['is_copyright'] == "t") {
657  $action = '';
658  if (array_key_exists('decluttered_content', $deactivatedCopyright) &&
659  !empty($deactivatedCopyright['decluttered_content'])) {
660  $content = $deactivatedCopyright['decluttered_content'];
661  } else {
662  // No text update. Nothing to do.
663  $this->heartbeat(1);
664  continue;
665  }
666  } else {
667  $action = 'delete';
668  }
669  $this->copyrightDao->updateTable($itemTreeBounds, $hash, $content, $this->userId, $cpTable, $action);
670  $this->heartbeat(1);
671  }
672  fclose($tmpFile);
673  }
674 
681  private function callCopyrightDeactivationClutterRemovalScript($tmpFilePath,
682  $clutter_flag): string
683  {
684  $script = "copyrightDeactivationClutterRemovalScript.py";
685  $args = ["python3", __DIR__ . "/$script", "--file", $tmpFilePath];
686  if ($clutter_flag) {
687  $args[] = "--clutter";
688  }
689 
690  $sleepTime = 5;
691  $maxSleepTime = 25;
692 
693  $process = new Process($args);
694  $process->setTimeout(null); // Remove timeout to run indefinitely.
695  $process->setEnv(set_python_path());
696  $process->start();
697 
698  do {
699  $this->heartbeat(0);
700  sleep($sleepTime);
701  if ($sleepTime < $maxSleepTime) {
702  $sleepTime += 5;
703  }
704  } while ($process->isRunning());
705 
706  echo $process->getErrorOutput();
707 
708  return $process->getOutput();
709  }
710 
716  private function getLicenseType($licenseType)
717  {
718  global $SysConf;
719  $licenseTypes = array_map('trim', explode(',',
720  $SysConf['SYSCONFIG']['LicenseTypes']));
721  if (in_array($licenseType, $licenseTypes)) {
722  return $licenseType;
723  }
724  return "";
725  }
726 
734  protected function noLicenseConflict($itemTreeBounds, $licenseMatches)
735  {
736  $shortnames = [];
737  foreach ($licenseMatches as $agentMatch) {
738  foreach ($agentMatch as $agentLicenseMatches) {
739  foreach ($agentLicenseMatches as $licenseMatch) {
740  $shortnames[$licenseMatch->getLicenseId()] = $licenseMatch
741  ->getLicenseRef()->getShortName();
742  }
743  }
744  }
745  foreach ($shortnames as $shortname) {
746  try {
747  if (! $this->compatibilityDao->getCompatibilityForFile($itemTreeBounds,
748  $shortname)) {
749  return false;
750  }
751  } catch (InvalidAgentStageException $_) {
752  return false;
753  }
754  }
755  return true;
756  }
757 
763  protected function allLicenseInType($licenseMatches)
764  {
765  $licenseTypes = [];
766  foreach ($licenseMatches as $agentMatch) {
767  foreach ($agentMatch as $agentLicenseMatches) {
768  foreach ($agentLicenseMatches as $licenseMatch) {
769  if (!array_key_exists($licenseMatch->getLicenseId(), $licenseTypes)) {
770  $licenseTypes[$licenseMatch->getLicenseId()] = $this->licenseDao
771  ->getLicenseType($licenseMatch->getLicenseId());
772  }
773  }
774  }
775  }
776  if (! in_array($this->licenseType, $licenseTypes)) {
777  return false;
778  }
779  if (! empty(array_diff($licenseTypes, [$this->licenseType]))) {
780  return false;
781  }
782  return true;
783  }
784 }
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()
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
Namespace for decider agent.
Definition: BulkReuser.php:8
$GLOBALS['xyyzzzDeciderJob']