FOSSology  4.7.0
Open Source License Compliance by Open Source Software
ObligationCsvImport.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2017 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
9 
13 
24 {
27  protected $dbManager;
30  protected $delimiter = ',';
33  protected $enclosure = '"';
36  protected $headrow = null;
39  protected $alias = array(
40  'type'=>array('type','Type'),
41  'topic'=>array('topic','Obligation or Risk topic'),
42  'text'=>array('text','Full Text'),
43  'classification'=>array('classification','Classification'),
44  'modifications'=>array('modifications','Apply on modified source code'),
45  'comment'=>array('comment','Comment'),
46  'licnames'=>array('licnames','Associated Licenses'),
47  'candidatenames'=>array('candidatenames','Associated candidate Licenses'),
48  'external_id'=>array('id','LicenseDB Id'),
49  );
50 
53  protected $obligationMap;
54 
60  {
61  $this->dbManager = $dbManager;
62  $this->obligationMap = $obligationMap;
63  }
64 
69  public function setDelimiter($delimiter=',')
70  {
71  $this->delimiter = substr($delimiter,0,1);
72  }
73 
78  public function setEnclosure($enclosure='"')
79  {
80  $this->enclosure = substr($enclosure,0,1);
81  }
82 
89  public function handleFile($filename, $fileExtension)
90  {
91  if (!is_file($filename) || ($handle = fopen($filename, 'r')) === false) {
92  return _('Internal error');
93  }
94  $cnt = -1;
95  $msg = '';
96  try {
97  if ($fileExtension == 'csv') {
98  while (($row = fgetcsv($handle,0,$this->delimiter,$this->enclosure)) !== false) {
99  $log = $this->handleCsv($row);
100  if (!empty($log)) {
101  $msg .= "$log\n";
102  }
103  $cnt++;
104  }
105  $msg .= _('Read csv').(": $cnt ")._('obligations');
106  } else {
107  $jsonContent = fread($handle, filesize($filename));
108  $data = json_decode($jsonContent, true);
109  if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
110  $msg .= "Error decoding JSON: " . json_last_error_msg() . "\n";
111  }
112  $msg = $this->importJsonData($data, $msg);
113  $msg .= _('Read json').(":". count($data) ." ")._('obligations');
114  }
115  } catch(\Exception $e) {
116  fclose($handle);
117  return $msg .= _('Error while parsing file').': '.$e->getMessage();
118  }
119  fclose($handle);
120  return $msg;
121  }
122 
129  function handleRowJson($row)
130  {
131  $newArray = array();
132  foreach ($row as $key => $value) {
133  $newKey = $key;
134  foreach ($this->alias as $aliasKey => $aliasValues) {
135  if (in_array($key, $aliasValues)) {
136  $newKey = $aliasKey;
137  break;
138  }
139  }
140  $newArray[$newKey] = $value;
141  }
142  return $newArray;
143  }
144 
151  private function handleCsv($row)
152  {
153  if ($this->headrow===null) {
154  $this->headrow = $this->handleHeadCsv($row);
155  return 'head okay';
156  }
157 
158  $mRow = array();
159  foreach (array('type','topic','text','classification','modifications','comment','licnames','candidatenames') as $needle) {
160  $mRow[$needle] = $row[$this->headrow[$needle]];
161  }
162 
163  return $this->handleCsvObligation($mRow);
164  }
165 
172  private function handleHeadCsv($row)
173  {
174  $headrow = array();
175  foreach (array('type','topic','text','classification','modifications','comment','licnames','candidatenames') as $needle) {
176  $col = ArrayOperation::multiSearch($this->alias[$needle], $row);
177  if (false === $col) {
178  throw new \Exception("Undetermined position of $needle");
179  }
180  $headrow[$needle] = $col;
181  }
182  return $headrow;
183  }
184 
190  private function getKeyFromTopicAndText($row)
191  {
192  $req = array($row['topic'], $row['text']);
193  $row = $this->dbManager->getSingleRow('SELECT ob_pk FROM obligation_ref WHERE ob_topic=$1 AND ob_md5=md5($2)',$req);
194  return ($row === false) ? false : $row['ob_pk'];
195  }
196 
205  private function compareLicList($exists, $listFromCsv, $candidate, $row)
206  {
207  $getList = $this->obligationMap->getLicenseList($exists, $candidate);
208  $listFromDb = $this->reArrangeString($getList);
209  $listFromCsv = $this->reArrangeString($listFromCsv);
210  return strcmp($listFromDb, $listFromCsv);
211  }
212 
219  private function reArrangeString($string)
220  {
221  $string = explode(";", $string);
222  sort($string);
223  return implode(",", $string);
224  }
225 
232  private function clearListFromDb($exists, $candidate)
233  {
234  $licId = 0;
235  $this->obligationMap->unassociateLicenseFromObligation($exists, $licId, $candidate);
236  return true;
237  }
238 
247  private function handleCsvObligation($row)
248  {
249  /* @var $dbManager DbManager */
251  $exists = $this->getKeyFromTopicAndText($row);
252  $associatedLicenses = "";
253  $candidateLicenses = "";
254  $msg = "";
255  if ($exists !== false) {
256  $msg = $this->updateOtherFields($exists, $row);
257  if ( $this->compareLicList($exists, $row['licnames'], false, $row) === 0 ) {
258  $msg .=" No Changes in AssociateLicense";
259  } else {
260  $this->clearListFromDb($exists, false);
261  if (!empty($row['licnames'])) {
262  $associatedLicenses .= $this->AssociateWithLicenses($row['licnames'], $exists, false);
263  }
264  $msg .=" Updated AssociatedLicense license";
265  }
266  if ($this->compareLicList($exists, $row['candidatenames'], true, $row) === 0) {
267  $msg .=" No Changes in CandidateLicense";
268  } else {
269  $this->clearListFromDb($exists, true);
270  if (!empty($row['candidatenames'])) {
271  $associatedLicenses .= $this->AssociateWithLicenses($row['candidatenames'], $exists, true);
272  }
273  $msg .=" Updated CandidateLicense";
274  }
275  return $msg . "\n" . $associatedLicenses . "\n";
276  }
277 
278  $stmtInsert = __METHOD__.'.insert';
279  $dbManager->prepare($stmtInsert,'INSERT INTO obligation_ref (ob_type,ob_topic,ob_text,ob_classification,ob_modifications,ob_comment,ob_md5)'
280  . ' VALUES ($1,$2,$3,$4,$5,$6,md5($3)) RETURNING ob_pk');
281  $resi = $dbManager->execute($stmtInsert,array($row['type'],$row['topic'],$row['text'],$row['classification'],$row['modifications'],$row['comment']));
282  $new = $dbManager->fetchArray($resi);
283  $dbManager->freeResult($resi);
284 
285  if (!empty($row['licnames'])) {
286  $associatedLicenses .= $this->AssociateWithLicenses($row['licnames'], $new['ob_pk']);
287  }
288  if (!empty($row['candidatenames'])) {
289  $candidateLicenses = $this->AssociateWithLicenses($row['candidatenames'], $new['ob_pk'], true);
290  }
291 
292  $message = "License association results for obligation '$row[topic]':\n";
293  $message .= "$associatedLicenses";
294  $message .= "$candidateLicenses";
295  $message .= "Obligation with id=$new[ob_pk] was added successfully.\n";
296  return $message;
297  }
298 
309  private function handleLicenseDBObligationImport($row)
310  {
311  if (empty($row["external_id"])) {
312  return "Error: external_id cannot be empty";
313  }
314  $stmt = __METHOD__ . ".getExistingObligation";
315  $obPk = $this->dbManager->getSingleRow("SELECT ob_pk FROM " .
316  "obligation_ref WHERE ob_external_id=$1", array($row["external_id"]), $stmt);
317  $log = '';
318  if (!empty($obPk)) {
319  // update obligation
320  $log .= $this->updateOtherFields($obPk['ob_pk'], $row);
321 
322  $this->dbManager->begin();
323  // fetch new license associations
324  $stmt = __METHOD__ . "getNewLicenseAssociations";
325  $newLicIds = array();
326  foreach ($row['license_ids'] as $licExternalId) {
327  $rfPk = $this->dbManager->getSingleRow("SELECT rf_pk FROM license_ref WHERE rf_external_id = $1;",
328  array($licExternalId), $stmt);
329  if (!empty($rfPk)) {
330  $newLicIds[] = $rfPk['rf_pk'];
331  } else {
332  $log .= \sprintf('license with rf_external_id %s not found', $licExternalId);
333  }
334  }
335 
336  // fetch old license associations
337  $stmt = __METHOD__ . "getOldLiceneAssociations";
338  $oldLicIdsDb = $this->dbManager->getRows("SELECT rf_fk FROM obligation_map WHERE ob_fk = $1", array($obPk['ob_pk']), $stmt);
339  $oldLicIds = array();
340  foreach ($oldLicIdsDb as $licId) {
341  $oldLicIds[] = $licId['rf_fk'];
342  }
343 
344  // create diff of licenses between current associated licenses and licensedb
345  // associated licenses
346  $diff = ArrayOperation::getArrayDiffs($oldLicIds, $newLicIds);
347 
348  if (!empty($diff['remove'])) {
349  $log .= ' removed license ids [' . implode(',', $diff['remove']) . ']';
350  }
351  if (!empty($diff['add'])) {
352  $log .= ' added license ids [' . implode(',', $diff['add']) . ']';
353  }
354 
355  // delete associations
356  $this->obligationMap->unassociateLicenseFromLicenseList($obPk['ob_pk'], $diff['remove']);
357 
358  // insert associations
359  $this->obligationMap->associateLicenseFromLicenseList($obPk['ob_pk'], $diff['add']);
360 
361  $this->dbManager->commit();
362  } else {
363  $stmtInsert = __METHOD__.'.insert';
364  $this->dbManager->prepare($stmtInsert,'INSERT INTO obligation_ref (ob_type,ob_topic,ob_text,ob_classification,ob_modifications,ob_comment,ob_md5,ob_external_id)'
365  . ' VALUES ($1,$2,$3,$4,$5,$6,md5($3),$7) RETURNING ob_pk');
366  $resi = $this->dbManager->execute($stmtInsert,array($row['type'],$row['topic'],$row['text'],$row['classification'],'False',$row['comment'], $row['external_id']));
367  $new = $this->dbManager->fetchArray($resi);
368  $this->dbManager->freeResult($resi);
369 
370  $this->dbManager->begin();
371  $newLicIds = array();
372  $stmt = __METHOD__ . "getNewLicensesForInsert";
373  foreach ($row['license_ids'] as $licExternalId) {
374  $rfPk = $this->dbManager->getSingleRow("SELECT rf_pk FROM license_ref WHERE rf_external_id = $1;",
375  array($licExternalId), $stmt);
376  if (!empty($rfPk)) {
377  $newLicIds[] = $rfPk['rf_pk'];
378  } else {
379  $message .= \sprintf('license with rf_external_id %s not found', $licExternalId);
380  }
381  }
382 
383  // insert associations
384  $this->obligationMap->associateLicenseFromLicenseList($new['ob_pk'], $newLicIds);
385  if (!empty($newLicIds)) {
386  $log .= ' added license ids [' . implode(',', $newLicIds) . ']';
387  }
388 
389  $this->dbManager->commit();
390  }
391  return $log;
392  }
393 
402  function AssociateWithLicenses($licList, $obPk, $candidate=False)
403  {
404  $associatedLicenses = "";
405  $message = "";
406 
407  $licenses = explode(";",$licList);
408  foreach ($licenses as $license) {
409  $licIds = $this->obligationMap->getIdFromShortname($license, $candidate);
410  $updated = false;
411  if (empty($licIds)) {
412  $message .= "License $license could not be found in the DB.\n";
413  } else {
414  $updated = $this->obligationMap->associateLicenseFromLicenseList($obPk,
415  $licIds, $candidate);
416  }
417  if ($updated) {
418  if ($associatedLicenses == "") {
419  $associatedLicenses = "$license";
420  } else {
421  $associatedLicenses .= ";$license";
422  }
423  }
424  }
425 
426  if (!empty($associatedLicenses)) {
427  $message .= "$associatedLicenses were associated.\n";
428  } else {
429  $message .= "No ";
430  $message .= $candidate ? "candidate": "";
431  $message .= "licenses were associated.\n";
432  }
433  return $message;
434  }
435 
452  function updateOtherFields($exists, $row)
453  {
454  $stmt = __METHOD__ . '.getOldObligation';
455  $oldObligation = $this->dbManager->getSingleRow(
456  'SELECT ob_topic, ob_text, ob_classification, ob_modifications, ob_comment, ob_type FROM obligation_ref WHERE ob_pk=$1',
457  array($exists),
458  $stmt
459  );
460 
461  if (empty($oldObligation)) {
462  return "Obligation with id=$exists not found";
463  }
464 
465  $log = "Obligation topic '$oldObligation[ob_topic]' already exists in DB (id = $exists)";
466  $stmt = __METHOD__ . '.updateOtherOb';
467  $sql = 'UPDATE obligation_ref SET ';
468  $extraParams = array();
469  $params = array($exists);
470 
471  if (!empty($row['external_id'])) {
472  if (isset($row['topic']) && $row['topic'] != $oldObligation['ob_topic']) {
473  $params[] = $row['topic'];
474  $stmt .= '.topic';
475  $extraParams[] = 'ob_topic=$' . count($params);
476  $log .= ', updated topic';
477  }
478  if (isset($row['text']) && $row['text'] != $oldObligation['ob_text']) {
479  $params[] = $row['text'];
480  $stmt .= '.text';
481  $extraParams[] = 'ob_text=$' . count($params) . ',ob_md5=md5($' .
482  count($params) . ')';
483  $log .= ', updated text';
484  }
485  if (isset($row['classification']) && $row['classification'] != $oldObligation['ob_classification']) {
486  $params[] = $row['classification'];
487  $stmt .= '.classification';
488  $extraParams[] = 'ob_classification=$' . count($params);
489  $log .= ', updated classification';
490  }
491  if (isset($row['comment']) && $row['comment'] != $oldObligation['ob_comment']) {
492  $params[] = $row['comment'];
493  $stmt .= '.comment';
494  $extraParams[] = 'ob_comment=$' . count($params);
495  $log .= ', updated comment';
496  }
497  if (isset($row['type']) && $row['type'] != $oldObligation['ob_type']) {
498  $params[] = $row['type'];
499  $stmt .= '.type';
500  $extraParams[] = 'ob_type=$' . count($params);
501  $log .= ', updated type';
502  }
503  } else {
504  if (isset($row['classification']) && $row['classification'] != $oldObligation['ob_classification']) {
505  $params[] = $row['classification'];
506  $stmt .= '.classification';
507  $extraParams[] = 'ob_classification=$' . count($params);
508  $log .= ', updated classification';
509  }
510  if (isset($row['modifications']) && $row['modifications'] != $oldObligation['ob_modifications']) {
511  $params[] = $row['modifications'];
512  $stmt .= '.modifications';
513  $extraParams[] = 'ob_modifications=$' . count($params);
514  $log .= ', updated modifications';
515  }
516  if (isset($row['comment']) && $row['comment'] != $oldObligation['ob_comment']) {
517  $params[] = $row['comment'];
518  $stmt .= '.comment';
519  $extraParams[] = 'ob_comment=$' . count($params);
520  $log .= ', updated comment';
521  }
522  }
523 
524  if (count($params) > 1) {
525  $sql .= join(',', $extraParams);
526  $sql .= ' WHERE ob_pk=$1';
527  $this->dbManager->getSingleRow($sql, $params, $stmt);
528  }
529 
530  return $log;
531  }
532 
538  public function importJsonData($data, string $msg): string
539  {
540  foreach ($data as $row) {
541  $log = $this->handleLicenseDBObligationImport($this->handleRowJson($row));
542  if (!empty($log)) {
543  $msg .= "$log\n";
544  }
545  }
546  return $msg;
547  }
548 }
Helper class for Obligation CSV Import.
setDelimiter($delimiter=',')
Update the delimiter.
setEnclosure($enclosure='"')
Update the enclosure.
clearListFromDb($exists, $candidate)
Clear all license maps for given obligation.
__construct(DbManager $dbManager, $obligationMap=null)
getKeyFromTopicAndText($row)
Get the Obligation key from obligation topic and obligation text.
handleCsvObligation($row)
Handle a single row from CSV.
updateOtherFields($exists, $row)
Update other fields of the obligation.
AssociateWithLicenses($licList, $obPk, $candidate=False)
Associate selected licenses to the obligation.
handleFile($filename, $fileExtension)
Read the CSV line by line and import it.
handleLicenseDBObligationImport($row)
Handle a single row from csv import form LicenseDB.
compareLicList($exists, $listFromCsv, $candidate, $row)
Compare licenses from Database and CSV.
Wrapper class for obligation map.
Fossology exception.
Definition: Exception.php:15
static getArrayDiffs(array $oldArray, array $newArray)
Get list of additions/removals to make $oldArray equal to $newArray.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
Utility functions for specific applications.