FOSSology  4.7.0-rc1
Open Source License Compliance by Open Source Software
ShowJobsDao.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2015-2018 Siemens AG
4  Author: Shaheem Azmal<shaheem.azmal@siemens.com>, Anupam Ghosh <anupam.ghosh@siemens.com>
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
8 
9 namespace Fossology\Lib\Dao;
10 
13 use Monolog\Logger;
14 
16 {
18  private $dbManager;
20  private $uploadDao;
22  private $logger;
24  private $maxJobsPerPage = 10; /* max number of jobs to display on a page */
26  private $nhours = 336; /* 336=24*14 (2 weeks) What is considered a recent number of hours for "My Recent Jobs" */
27 
28  function __construct(DbManager $dbManager, UploadDao $uploadDao)
29  {
30  $this->dbManager = $dbManager;
31  $this->uploadDao = $uploadDao;
32  $this->logger = new Logger(self::class);
33  }
34 
40  private function toPgIntArray(array $ints)
41  {
42  return '{' . implode(',', array_map('intval', $ints)) . '}';
43  }
44 
53  function uploads2Jobs($upload_pks, $page = 0)
54  {
55  $jobArray = array();
56 
57  // only use the uploads the user / group has access to
58  $upload_pks = $this->uploadDao->filterAccessibleUploads($upload_pks, Auth::getGroupId());
59 
60  if (empty($upload_pks)) {
61  return $jobArray;
62  }
63 
64  $upload_pks = array_values($upload_pks);
65  $uploadList = $this->toPgIntArray($upload_pks);
66 
67  /* Count total jobs for pagination */
68  $countRow = $this->dbManager->getSingleRow(
69  "SELECT count(*) AS cnt FROM job WHERE job_upload_fk = ANY($1)",
70  array($uploadList),
71  __METHOD__ . ".countJobs"
72  );
73  $totalJobCount = intval($countRow['cnt']);
74  $totalPages = floor($totalJobCount / $this->maxJobsPerPage);
75 
76  /* Fetch paginated jobs */
77  $offset = empty($page) ? 0 : $page * $this->maxJobsPerPage;
78  $statementName = __METHOD__ . ".paginatedJobs";
79  $this->dbManager->prepare($statementName,
80  "SELECT job_pk FROM job WHERE job_upload_fk = ANY($1) " .
81  "ORDER BY job_pk DESC LIMIT $2 OFFSET $3");
82  $result = $this->dbManager->execute($statementName,
83  array($uploadList, $this->maxJobsPerPage, $offset));
84 
85  while ($row = $this->dbManager->fetchArray($result)) {
86  $jobArray[] = $row['job_pk'];
87  }
88  $this->dbManager->freeResult($result);
89 
90  return array($jobArray, $totalPages);
91  } /* uploads2Jobs() */
92 
98  public function getJobName($uploadId)
99  {
100  $statementName = __METHOD__."forjob_name";
101  /* upload has been deleted so try to get the job name from the original upload job record */
102  $row = $this->dbManager->getSingleRow(
103  "SELECT job_name FROM job WHERE job_upload_fk= $1 ORDER BY job_pk ASC",
104  array($uploadId),
105  $statementName
106  );
107  return (empty($row['job_name']) ? $uploadId : $row['job_name']);
108  } /* getJobName */
109 
118  public function myJobs($allusers, $page = 0)
119  {
120  $jobArray = array();
121  $offset = empty($page) ? 0 : ($page * $this->maxJobsPerPage) - 1;
122 
123  $allusers_str = ($allusers == 0) ? "job_user_fk='" . Auth::getUserId() .
124  "' and " : "";
125 
126  $statementName = __METHOD__ . "." . $allusers_str;
127  $this->dbManager->prepare($statementName,
128  "SELECT job_pk, job_upload_fk FROM job " . "WHERE $allusers_str " .
129  "job_queued >= (now() - interval '" . $this->nhours . " hours') " .
130  "ORDER BY job_queued DESC");
131  $result = $this->dbManager->execute($statementName);
132 
133  $rows = array();
134  $uploadIds = array();
135  while ($row = $this->dbManager->fetchArray($result)) {
136  $rows[] = $row;
137  if (!empty($row['job_upload_fk'])) {
138  $uploadIds[] = intval($row['job_upload_fk']);
139  }
140  }
141  $this->dbManager->freeResult($result);
142 
143  $accessibleUploads = $this->uploadDao->filterAccessibleUploads(array_unique($uploadIds), Auth::getGroupId());
144 
145  foreach ($rows as $row) {
146  if (!empty($row['job_upload_fk'])) {
147  if (!in_array(intval($row['job_upload_fk']), $accessibleUploads)) {
148  continue;
149  }
150  }
151  $jobArray[] = $row['job_pk'];
152  }
153 
154  // calculate total pages for jobs accessible to current group
155  $totalPages = floor(count($jobArray) / $this->maxJobsPerPage);
156 
157  // get jobs for current page only
158  $pageJobs = array_slice($jobArray, $offset, $this->maxJobsPerPage);
159 
160  return array($pageJobs, $totalPages);
161  } /* myJobs() */
162 
188  public function getJobInfo($job_pks)
189  {
190  if (empty($job_pks)) {
191  return array();
192  }
193 
194  $jobPkArray = $this->toPgIntArray($job_pks);
195 
196  $jobData = $this->fetchJobRecords($jobPkArray);
197 
198  list($uploadRows, $deletedMap) = $this->fetchUploadData($jobPkArray);
199  $uploadtreeRows = $this->fetchUploadtreeRoots($uploadRows);
200 
201  $this->attachUploadData($jobData, $uploadRows, $uploadtreeRows, $deletedMap);
202  $this->attachJobQueueData($jobData, $jobPkArray);
203 
204  // Remove jobs with no jobqueue entries
205  foreach ($jobData as $job_pk => $data) {
206  if (!isset($data['jobqueue'])) {
207  unset($jobData[$job_pk]);
208  }
209  }
210 
211  return $jobData;
212  } /* getJobInfo() */
213 
219  private function fetchJobRecords($jobPkArray)
220  {
221  $jobData = array();
222  $stmt = __METHOD__;
223  $this->dbManager->prepare($stmt,
224  "SELECT * FROM job WHERE job_pk = ANY($1::int[])");
225  $result = $this->dbManager->execute($stmt, array($jobPkArray));
226  foreach ($this->dbManager->fetchAll($result) as $row) {
227  $jobData[$row['job_pk']]['job'] = $row;
228  }
229  $this->dbManager->freeResult($result);
230  return $jobData;
231  }
232 
238  private function fetchUploadData($jobPkArray)
239  {
240  $stmt = __METHOD__;
241  $this->dbManager->prepare($stmt,
242  "SELECT u.*, j.job_pk, j.job_upload_fk AS jb_upload_fk
243  FROM job j
244  LEFT JOIN upload u ON u.upload_pk = j.job_upload_fk
245  WHERE j.job_pk = ANY($1::int[])
246  AND j.job_upload_fk IS NOT NULL");
247  $result = $this->dbManager->execute($stmt, array($jobPkArray));
248 
249  $uploadRows = array();
250  $deletedMap = array();
251  foreach ($this->dbManager->fetchAll($result) as $row) {
252  $jobPk = $row['job_pk'];
253  $uploadFk = intval($row['jb_upload_fk']);
254  unset($row['job_pk'], $row['jb_upload_fk']);
255 
256  if (!empty($row['upload_pk'])) {
257  $uploadRows[$row['upload_pk']] = $row;
258  } else {
259  $deletedMap[$jobPk] = $uploadFk;
260  }
261  }
262  $this->dbManager->freeResult($result);
263  return array($uploadRows, $deletedMap);
264  }
265 
271  private function fetchUploadtreeRoots($uploadRows)
272  {
273  $uploadsByTablename = array();
274  foreach ($uploadRows as $rec) {
275  $tablename = $rec['uploadtree_tablename'];
276  $uploadsByTablename[$tablename][] = intval($rec['upload_pk']);
277  }
278 
279  $rows = array();
280  foreach ($uploadsByTablename as $tablename => $pks) {
281  if (!$this->dbManager->existsTable($tablename)) {
282  continue;
283  }
284  $pkArray = $this->toPgIntArray($pks);
285  $stmt = __METHOD__ . ".$tablename";
286  $this->dbManager->prepare($stmt,
287  "SELECT * FROM $tablename WHERE upload_fk = ANY($1::int[]) AND parent IS NULL");
288  $result = $this->dbManager->execute($stmt, array($pkArray));
289  foreach ($this->dbManager->fetchAll($result) as $row) {
290  $rows[$row['upload_fk']] = $row;
291  }
292  $this->dbManager->freeResult($result);
293  }
294  return $rows;
295  }
296 
305  private function attachUploadData(&$jobData, $uploadRows, $uploadtreeRows, $deletedMap)
306  {
307  foreach ($jobData as $job_pk => &$data) {
308  $upload_pk = $data['job']['job_upload_fk'] ?? null;
309  if (empty($upload_pk)) {
310  continue;
311  }
312  if (isset($uploadRows[$upload_pk])) {
313  $data['upload'] = $uploadRows[$upload_pk];
314  $data['uploadtree'] = $uploadtreeRows[$upload_pk] ?? null;
315  } elseif (isset($deletedMap[$job_pk])) {
316  $uploadFk = $deletedMap[$job_pk];
317  $jobName = $this->getJobName($uploadFk);
318  $data['upload'] = array(
319  'upload_filename' => "Deleted Upload: " . $uploadFk . "(" . $jobName . ")",
320  'upload_pk' => $uploadFk,
321  );
322  }
323  }
324  unset($data);
325  }
326 
332  private function attachJobQueueData(&$jobData, $jobPkArray)
333  {
334  $stmt = __METHOD__;
335  $this->dbManager->prepare($stmt,
336  "SELECT jq.*, jd.jdep_jq_depends_fk
337  FROM jobqueue jq
338  LEFT OUTER JOIN jobdepends jd ON jq.jq_pk = jd.jdep_jq_fk
339  WHERE jq.jq_job_fk = ANY($1::int[])
340  ORDER BY jq.jq_pk ASC");
341  $result = $this->dbManager->execute($stmt, array($jobPkArray));
342 
343  foreach ($this->dbManager->fetchAll($result) as $jobQueueRec) {
344  $job_pk = $jobQueueRec['jq_job_fk'];
345  $jq_pk = $jobQueueRec['jq_pk'];
346 
347  if (!isset($jobData[$job_pk])) {
348  continue;
349  }
350 
351  if (isset($jobData[$job_pk]['jobqueue'][$jq_pk])) {
352  $jobData[$job_pk]['jobqueue'][$jq_pk]['depends'][] = $jobQueueRec['jdep_jq_depends_fk'];
353  } else {
354  $jobQueueRec['depends'] = array($jobQueueRec['jdep_jq_depends_fk']);
355  $jobData[$job_pk]['jobqueue'][$jq_pk] = $jobQueueRec;
356  }
357  }
358  $this->dbManager->freeResult($result);
359  }
360 
367  public function getNumItemsPerSec($itemsprocessed, $numSecs)
368  {
369  return ($numSecs > 0) ? $itemsprocessed/$numSecs : 0;
370  }
371 
380  public function getEstimatedTime($job_pk, $jq_Type='', $filesPerSec=0, $uploadId=0, $timeInSec=0)
381  {
382  if (!empty($uploadId)) {
383  $itemCount = $this->dbManager->getSingleRow(
384  "SELECT jq_itemsprocessed FROM jobqueue INNER JOIN job ON jq_job_fk=job_pk "
385  . " WHERE jq_type LIKE 'ununpack' AND jq_end_bits ='1' AND job_upload_fk=$1",
386  array($uploadId),
387  __METHOD__.'.ununpack_might_be_in_other_job'
388  );
389  } else {
390  $itemCount = $this->dbManager->getSingleRow(
391  "SELECT jq_itemsprocessed FROM jobqueue WHERE jq_type LIKE 'ununpack' AND jq_end_bits ='1' AND jq_job_fk =$1",
392  array($job_pk),
393  __METHOD__.'.ununpack_must_be_in_this_job'
394  );
395  }
396 
397  if (!empty($itemCount['jq_itemsprocessed']) && $jq_Type !== 'decider') {
398 
399  $selectCol = "jq_type, jq_endtime, jq_starttime, jq_itemsprocessed";
400  if (empty($jq_Type)) {
401  $removeType = "jq_type NOT LIKE 'ununpack' AND jq_type NOT LIKE 'reportgen' AND jq_type NOT LIKE 'decider' AND jq_type NOT LIKE 'softwareHeritage' AND";
402  /* get starttime endtime and jobtype form jobqueue for a jobid except $removeType */
403  $statementName = __METHOD__."$selectCol.$removeType";
404  $this->dbManager->prepare($statementName,
405  "SELECT $selectCol FROM jobqueue WHERE $removeType jq_job_fk =$1 ORDER BY jq_type DESC");
406  } else {
407  $statementName = __METHOD__."$selectCol.$jq_Type";
408  $this->dbManager->prepare($statementName,
409  "SELECT $selectCol FROM jobqueue WHERE jq_type LIKE '$jq_Type' AND jq_job_fk =$1");
410  }
411  $result = $this->dbManager->execute($statementName, array($job_pk));
412  $estimatedArray = array(); // estimate time for each agent
413 
414  while ($row = $this->dbManager->fetchArray($result)) {
415  $timeOfCompletion = 0;
416  if (empty($row['jq_endtime']) && !empty($row['jq_starttime'])) { // for agent started and not ended
417  if (empty($filesPerSec)) {
418  $burnTime = time() - strtotime($row['jq_starttime']);
419  $filesPerSec = $this->getNumItemsPerSec($row['jq_itemsprocessed'], $burnTime);
420  }
421 
422  if (!empty($filesPerSec)) {
423  $timeOfCompletion = ($itemCount['jq_itemsprocessed'] - $row['jq_itemsprocessed']) / $filesPerSec;
424  }
425  $estimatedArray[] = $timeOfCompletion;
426  }
427  }
428  if (empty($estimatedArray)) {
429  return "";
430  } else {
431  $estimatedTime = round(max($estimatedArray)); // collecting max agent time in seconds
432  if (!empty($timeInSec)) {
433  return intval(!empty($estimatedTime) ? $estimatedTime : 0);
434  }
435  return intval($estimatedTime/3600).gmdate(":i:s", $estimatedTime); // convert seconds to time and return
436  }
437  }
438  }/* getEstimatedTime() */
439 
445  public function getDataForASingleJob($jq_pk)
446  {
447  $statementName = __METHOD__."getDataForASingleJob";
448  $this->dbManager->prepare($statementName,
449  "SELECT *, jq_endtime-jq_starttime as elapsed FROM jobqueue LEFT JOIN job ON job.job_pk = jobqueue.jq_job_fk WHERE jobqueue.jq_pk =$1");
450  $result = $this->dbManager->execute($statementName, array($jq_pk));
451  $row = $this->dbManager->fetchArray($result);
452  $this->dbManager->freeResult($result);
453  return $row;
454  } /* getDataForASingleJob */
455 
460  public function getJobStatus($jqPk)
461  {
462  $statementName = __METHOD__."forjq_pk";
463  $row = $this->dbManager->getSingleRow(
464  "SELECT jq_end_bits FROM jobqueue WHERE jq_pk = $1",
465  array($jqPk),
466  $statementName
467  );
468  if ($row['jq_end_bits'] == 1 || $row['jq_end_bits'] == 2) {
469  return false;
470  } else {
471  return true;
472  }
473  }
474 
480  public function getItemsProcessedForDecider($jqType, $jobId)
481  {
482  $statementName = __METHOD__."forjqTypeAndjobId";
483  $row = $this->dbManager->getSingleRow(
484  "SELECT jq_itemsprocessed, job.job_upload_fk FROM jobqueue JOIN job ON jobqueue.jq_job_fk = job.job_pk WHERE jq_type = $1 AND jq_end_bits = 0 AND jq_job_fk IN (SELECT job_pk FROM job WHERE job_upload_fk = (SELECT job_upload_fk FROM job WHERE job_pk = $2 LIMIT 1)) LIMIT 1",
485  array($jqType, $jobId),
486  $statementName
487  );
488  if (!empty($row['jq_itemsprocessed'])) {
489  return array($row['jq_itemsprocessed'], $row['job_upload_fk']);
490  } else {
491  return array();
492  }
493  }
494 
499  public function getJobsForAll()
500  {
501  $sql = "SELECT jq_type AS job, jq_job_fk, job_upload_fk AS upload_fk, " .
502  "CASE WHEN (jq_endtext IS NULL AND jq_end_bits = 0) THEN 'pending' " .
503  "WHEN (jq_endtext = ANY('{Started,Restarted,Paused}')) THEN 'running' " .
504  "ELSE '' END AS status " .
505  "FROM jobqueue INNER JOIN job " .
506  "ON jq_job_fk = job_pk " .
507  "AND job_queued >= (now() - interval '" . $this->nhours . " hours') " .
508  "WHERE jq_endtime IS NULL;";
509  $statement = __METHOD__ . ".getAllUnFinishedJobs";
510  return $this->dbManager->getRows($sql, [], $statement);
511  }
512 
517  public function isJobIdPresentInReportGen($jobId)
518  {
519  $statementName = __METHOD__ . "forJobIdCheck";
520  $row = $this->dbManager->getSingleRow(
521  "SELECT 1 FROM reportgen WHERE job_fk = $1",
522  array($jobId),
523  $statementName
524  );
525  return !empty($row);
526  }
527 }
Contains the constants and helpers for authentication of user.
Definition: Auth.php:24
static getUserId()
Get the current user's id.
Definition: Auth.php:68
static getGroupId()
Get the current user's group id.
Definition: Auth.php:80
fetchUploadData($jobPkArray)
Fetch both existing uploads and jobs referencing deleted uploads.
uploads2Jobs($upload_pks, $page=0)
Find all the jobs for a given set of uploads.
Definition: ShowJobsDao.php:53
getJobName($uploadId)
Return job name. Used for deleted jobs.
Definition: ShowJobsDao.php:98
toPgIntArray(array $ints)
Convert an array of integers to a Postgres array literal string.
Definition: ShowJobsDao.php:40
fetchJobRecords($jobPkArray)
Fetch job records for the given job_pk's.
getItemsProcessedForDecider($jqType, $jobId)
fetchUploadtreeRoots($uploadRows)
Fetch root records from uploadtree tables for the given upload records.
myJobs($allusers, $page=0)
Find all of my jobs submitted within the last n hours.
attachJobQueueData(&$jobData, $jobPkArray)
Attach jobqueue data to job data for the given job_pk's.
getEstimatedTime($job_pk, $jq_Type='', $filesPerSec=0, $uploadId=0, $timeInSec=0)
Returns Estimated time using jobid.
getDataForASingleJob($jq_pk)
Return total Job data with time elapsed.
getNumItemsPerSec($itemsprocessed, $numSecs)
Returns Number of files/items processed per sec.
getJobInfo($job_pks)
Get job queue data from db.
attachUploadData(&$jobData, $uploadRows, $uploadtreeRows, $deletedMap)
Attach upload and uploadtree data to job data. Also handles deleted uploads using the pre-fetched del...
FUNCTION int max(int permGroup, int permPublic)
Get the maximum group privilege.
Definition: libfossagent.c:295
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
list_t type structure used to keep various lists. (e.g. there are multiple lists).
Definition: nomos.h:308