FOSSology  3.2.0rc1
Open Source License Compliance by Open Source Software
LicenseCsvImport.php
Go to the documentation of this file.
1 <?php
2 /*
3 Copyright (C) 2014-2015, Siemens AG
4 
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 version 2 as published by the Free Software Foundation.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18 
20 
25 
36 {
39  protected $dbManager;
42  protected $userDao;
45  protected $delimiter = ',';
48  protected $enclosure = '"';
51  protected $headrow = null;
54  protected $nkMap = array();
57  protected $mdkMap = array();
60  protected $alias = array(
61  'shortname'=>array('shortname','Short Name'),
62  'fullname'=>array('fullname','Long Name'),
63  'text'=>array('text','Full Text'),
64  'parent_shortname'=>array('parent_shortname','Decider Short Name'),
65  'report_shortname'=>array('report_shortname','Regular License Text Short Name'),
66  'url'=>array('url','URL'),
67  'notes'=>array('notes'),
68  'source'=>array('source','Foreign ID'),
69  'risk'=>array('risk','risk_level'),
70  'group'=>array('group','License group')
71  );
72 
79  {
80  $this->dbManager = $dbManager;
81  $this->userDao = $userDao;
82  }
83 
88  public function setDelimiter($delimiter=',')
89  {
90  $this->delimiter = substr($delimiter,0,1);
91  }
92 
97  public function setEnclosure($enclosure='"')
98  {
99  $this->enclosure = substr($enclosure,0,1);
100  }
101 
108  public function handleFile($filename)
109  {
110  if (!is_file($filename) || ($handle = fopen($filename, 'r')) === false) {
111  return _('Internal error');
112  }
113  $cnt = -1;
114  $msg = '';
115  try {
116  while (($row = fgetcsv($handle,0,$this->delimiter,$this->enclosure)) !== false) {
117  $log = $this->handleCsv($row);
118  if (!empty($log)) {
119  $msg .= "$log\n";
120  }
121  $cnt++;
122  }
123  $msg .= _('Read csv').(": $cnt ")._('licenses');
124  } catch(\Exception $e) {
125  fclose($handle);
126  return $msg .= _('Error while parsing file').': '.$e->getMessage();
127  }
128  fclose($handle);
129  return $msg;
130  }
131 
138  private function handleCsv($row)
139  {
140  if ($this->headrow === null) {
141  $this->headrow = $this->handleHeadCsv($row);
142  return 'head okay';
143  }
144 
145  $mRow = array();
146  foreach (array('shortname','fullname','text') as $needle) {
147  $mRow[$needle] = $row[$this->headrow[$needle]];
148  }
149  foreach (array('parent_shortname' => null, 'report_shortname' => null,
150  'url' => '', 'notes' => '', 'source' => '', 'risk' => 0,
151  'group' => null) as $optNeedle=>$defaultValue) {
152  $mRow[$optNeedle] = $defaultValue;
153  if ($this->headrow[$optNeedle]!==false && array_key_exists($this->headrow[$optNeedle], $row)) {
154  $mRow[$optNeedle] = $row[$this->headrow[$optNeedle]];
155  }
156  }
157 
158  return $this->handleCsvLicense($mRow);
159  }
160 
167  private function handleHeadCsv($row)
168  {
169  $headrow = array();
170  foreach (array('shortname','fullname','text') as $needle) {
171  $col = ArrayOperation::multiSearch($this->alias[$needle], $row);
172  if (false === $col) {
173  throw new \Exception("Undetermined position of $needle");
174  }
175  $headrow[$needle] = $col;
176  }
177  foreach (array('parent_shortname', 'report_shortname', 'url', 'notes',
178  'source', 'risk', 'group') as $optNeedle) {
179  $headrow[$optNeedle] = ArrayOperation::multiSearch($this->alias[$optNeedle], $row);
180  }
181  return $headrow;
182  }
183 
190  private function updateLicense($row, $rfPk)
191  {
192  $stmt = __METHOD__ . '.getOldLicense';
193  $oldLicense = $this->dbManager->getSingleRow('SELECT ' .
194  'rf_shortname, rf_fullname, rf_text, rf_url, rf_notes, rf_source, rf_risk ' .
195  'FROM license_ref WHERE rf_pk = $1', array($rfPk), $stmt);
196 
197  $stmt = __METHOD__ . '.getOldMapping';
198  $sql = 'SELECT rf_parent FROM license_map WHERE rf_fk = $1 AND usage = $2;';
199  $oldParent = null;
200  $oldParentRow = $this->dbManager->getSingleRow($sql, array($rfPk,
201  LicenseMap::CONCLUSION), $stmt);
202  if (!empty($oldParentRow)) {
203  $oldParent = $oldParentRow['rf_parent'];
204  }
205  $oldReport = null;
206  $oldReportRow = $this->dbManager->getSingleRow($sql, array($rfPk,
207  LicenseMap::REPORT), $stmt);
208  if (!empty($oldReportRow)) {
209  $oldReport = $oldReportRow['rf_parent'];
210  }
211 
212  $newParent = null;
213  $newParent = ($row['parent_shortname'] == null) ? null :
214  $this->getKeyFromShortname($row['parent_shortname']);
215 
216  $newReport = null;
217  $newReport = ($row['report_shortname'] == null) ? null :
218  $this->getKeyFromShortname($row['report_shortname']);
219 
220  $log = "License '$row[shortname]' already exists in DB (id = $rfPk)";
221  $stmt = __METHOD__ . '.updateLicense';
222  $sql = "UPDATE license_ref SET ";
223  if (! empty($row['group'])) {
224  $sql = "UPDATE license_candidate SET ";
225  }
226  $extraParams = array();
227  $param = array($rfPk);
228  if (!empty($row['fullname']) && $row['fullname'] != $oldLicense['rf_fullname']) {
229  $param[] = $row['fullname'];
230  $stmt .= '.fullN';
231  $extraParams[] = "rf_fullname=$" . count($param);
232  $log .= ", updated fullname";
233  }
234  if (!empty($row['text']) && $row['text'] != $oldLicense['rf_text']) {
235  $param[] = $row['text'];
236  $stmt .= '.text';
237  $extraParams[] = "rf_text=$" . count($param) . ",rf_md5=md5($" .
238  count($param) . ")";
239  $log .= ", updated text";
240  }
241  if (!empty($row['url']) && $row['url'] != $oldLicense['rf_url']) {
242  $param[] = $row['url'];
243  $stmt .= '.url';
244  $extraParams[] = "rf_url=$" . count($param);
245  $log .= ", updated URL";
246  }
247  if (!empty($row['notes']) && $row['notes'] != $oldLicense['rf_notes']) {
248  $param[] = $row['notes'];
249  $stmt .= '.notes';
250  $extraParams[] = "rf_notes=$" . count($param);
251  $log .= ", updated notes";
252  }
253  if (!empty($row['source']) && $row['source'] != $oldLicense['rf_source']) {
254  $param[] = $row['source'];
255  $stmt .= '.updSource';
256  $extraParams[] = "rf_source=$".count($param);
257  $log .= ', updated the source';
258  }
259  if (!empty($row['risk']) && $row['risk'] != $oldLicense['rf_risk']) {
260  $param[] = $row['risk'];
261  $stmt .= '.updRisk';
262  $extraParams[] = "rf_risk=$".count($param);
263  $log .= ', updated the risk level';
264  }
265  if (count($param) > 1) {
266  $sql .= join(",", $extraParams);
267  $sql .= " WHERE rf_pk=$1;";
268  $this->dbManager->getSingleRow($sql, $param, $stmt);
269  $this->mdkMap[md5($row['text'])] = $rfPk;
270  }
271 
272  if (($oldParent != $newParent) && $this->setMap($newParent, $rfPk, LicenseMap::CONCLUSION)) {
273  $log .= " with conclusion '$row[parent_shortname]'";
274  }
275  if (($oldReport != $newReport) && $this->setMap($newReport, $rfPk, LicenseMap::REPORT)) {
276  $log .= " reporting '$row[report_shortname]'";
277  }
278  return $log;
279  }
280 
289  private function handleCsvLicense($row)
290  {
291  if (empty($row['risk'])) {
292  $row['risk'] = 0;
293  }
294  $rfPk = $this->getKeyFromShortname($row['shortname'], $row['group']);
295  $md5Match = $this->getKeyFromMd5($row['text']);
296 
297  // If shortname exists, does not collide with other texts and is not
298  // candidate
299  if ($rfPk !== false) {
300  if (! empty($row['group']) || ($md5Match == $rfPk || $md5Match === false)) {
301  return $this->updateLicense($row, $rfPk);
302  } else {
303  return "Error: MD5 checksum of '" . $row['shortname'] .
304  "' collides with license id=$md5Match";
305  }
306  }
307  if ($md5Match !== false && empty($row['group'])) {
308  return "Error: MD5 checksum of '" . $row['shortname'] .
309  "' collides with license id=$md5Match";
310  }
311 
312  $return = "";
313  if (!empty($row['group'])) {
314  $return = $this->insertNewLicense($row, "license_candidate");
315  } else {
316  $return = $this->insertNewLicense($row, "license_ref");
317  }
318  return $return;
319  }
320 
332  private function insertMapIfNontrivial($fromName,$toName,$usage)
333  {
334  $isNontrivial = ($fromName!==null && $fromName!=$toName && $this->getKeyFromShortname($fromName)!==false);
335  if ($isNontrivial) {
336  $this->dbManager->insertTableRow('license_map',
337  array('rf_fk'=>$this->getKeyFromShortname($toName),
338  'rf_parent'=>$this->getKeyFromShortname($fromName),
339  'usage'=> $usage));
340  }
341  return $isNontrivial;
342  }
343 
349  private function getKeyFromShortname($shortname, $groupFk = null)
350  {
351  $keyName = $shortname;
352  $tableName = "license_ref";
353  $addCondition = "";
354  $statement = __METHOD__ . ".getId";
355  $params = array($shortname);
356 
357  if ($groupFk != null) {
358  $keyName .= $groupFk;
359  $tableName = "license_candidate";
360  $addCondition = "AND group_fk = $2";
361  $statement .= ".candidate";
362  $params[] = $this->userDao->getGroupIdByName($groupFk);
363  }
364  $sql = "SELECT rf_pk FROM ONLY $tableName WHERE rf_shortname = $1 $addCondition;";
365  if (array_key_exists($keyName, $this->nkMap)) {
366  return $this->nkMap[$keyName];
367  }
368  $row = $this->dbManager->getSingleRow($sql, $params, $statement);
369  $this->nkMap[$keyName] = ($row===false) ? false : $row['rf_pk'];
370  return $this->nkMap[$keyName];
371  }
372 
378  private function getKeyFromMd5($licenseText)
379  {
380  $md5 = md5($licenseText);
381  if (array_key_exists($md5, $this->mdkMap)) {
382  return $this->mdkMap[$md5];
383  }
384  $row = $this->dbManager->getSingleRow("SELECT rf_pk " .
385  "FROM ONLY license_ref WHERE rf_md5=md5($1)",
386  array($licenseText));
387  $this->mdkMap[$md5] = (empty($row)) ? false : $row['rf_pk'];
388  return $this->mdkMap[$md5];
389  }
390 
401  private function setMap($from, $to, $usage)
402  {
403  $return = false;
404  if (!empty($from)) {
405  $sql = "SELECT license_map_pk, rf_parent FROM license_map WHERE rf_fk = $1 AND usage = $2;";
406  $statement = __METHOD__ . ".getCurrentMapping";
407  $row = $this->dbManager->getSingleRow($sql, array($to, $usage), $statement);
408  if (!empty($row) && $row['rf_parent'] != $from) {
409  $this->dbManager->updateTableRow("license_map", array(
410  'rf_fk' => $to,
411  'rf_parent' => $from,
412  'usage' => $usage
413  ), 'license_map_pk', $row['license_map_pk']);
414  $return = true;
415  } elseif (empty($row)) {
416  $this->dbManager->insertTableRow('license_map', array(
417  'rf_fk' => $to,
418  'rf_parent' => $from,
419  'usage' => $usage
420  ));
421  $return = true;
422  }
423  }
424  return $return;
425  }
426 
436  private function insertNewLicense($row, $tableName = "license_ref")
437  {
438  $stmtInsert = __METHOD__ . '.insert.' . $tableName;
439  $columns = array(
440  "rf_shortname" => $row['shortname'],
441  "rf_fullname" => $row['fullname'],
442  "rf_text" => $row['text'],
443  "rf_md5" => md5($row['text']),
444  "rf_detector_type" => 1,
445  "rf_url" => $row['url'],
446  "rf_notes" => $row['notes'],
447  "rf_source" => $row['source'],
448  "rf_risk" => $row['risk']
449  );
450 
451  $as = "";
452  if ($tableName == "license_candidate") {
453  $groupId = $this->userDao->getGroupIdByName($row['group']);
454  if (empty($groupId)) {
455  return "Error: Unable to insert candidate license " . $row['shortname'] .
456  " as group " . $row['group'] . " does not exist";
457  }
458  $columns["group_fk"] = $groupId;
459  $columns["marydone"] = $this->dbManager->booleanToDb(true);
460  $as = " as candidate license under group " . $row["group"];
461  }
462 
463  $newPk = $this->dbManager->insertTableRow($tableName, $columns, $stmtInsert, 'rf_pk');
464 
465  if ($tableName == "license_candidate") {
466  $this->nkMap[$row['shortname'].$row['group']] = $newPk;
467  } else {
468  $this->nkMap[$row['shortname']] = $newPk;
469  }
470  $this->mdkMap[md5($row['text'])] = $newPk;
471  $return = "Inserted '$row[shortname]' in DB" . $as;
472 
473  if ($this->insertMapIfNontrivial($row['parent_shortname'], $row['shortname'], LicenseMap::CONCLUSION)) {
474  $return .= " with conclusion '$row[parent_shortname]'";
475  }
476  if ($this->insertMapIfNontrivial($row['report_shortname'], $row['shortname'], LicenseMap::REPORT)) {
477  $return .= " reporting '$row[report_shortname]'";
478  }
479  return $return;
480  }
481 }
handleFile($filename)
Read the CSV line by line and import it.
getKeyFromShortname($shortname, $groupFk=null)
Get the license id using license shortname from DB or nkMap.
updateLicense($row, $rfPk)
Update the license info in the DB.
Utility functions for specific applications.
setMap($from, $to, $usage)
Update license mappings.
insertMapIfNontrivial($fromName, $toName, $usage)
Insert in license_map table if the license conclusion is non-trivial.
__construct(DbManager $dbManager, UserDao $userDao)
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:28
Fossology exception.
Definition: Exception.php:25
handleCsvLicense($row)
Handle a single row from CSV.
setDelimiter($delimiter=',')
Update the delimiter.
setEnclosure($enclosure='"')
Update the enclosure.
insertNewLicense($row, $tableName="license_ref")
Insert a new license in DB.
handleHeadCsv($row)
Handle a row as head row.