Passed
Push — master ( 1aee7b...74a369 )
by Marcel
02:46
created

ExternalFile::getId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * Analytics
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the LICENSE.md file.
7
 *
8
 * @author Marcel Scherello <[email protected]>
9
 * @copyright 2019-2022 Marcel Scherello
10
 */
11
12
namespace OCA\Analytics\Datasource;
13
14
use OCP\IL10N;
15
use Psr\Log\LoggerInterface;
16
use OCA\Analytics\Service\VariableService;
17
18
class ExternalFile implements IDatasource
19
{
20
    private $logger;
21
    private $userId;
22
    private $l10n;
23
    private $VariableService;
24
25
    public function __construct(
26
        $userId,
27
        IL10N $l10n,
28
        LoggerInterface $logger,
29
        VariableService $VariableService
30
    )
31
    {
32
        $this->userId = $userId;
33
        $this->l10n = $l10n;
34
        $this->logger = $logger;
35
        $this->VariableService = $VariableService;
36
    }
37
38
    /**
39
     * @return string Display Name of the datasource
40
     */
41
    public function getName(): string
42
    {
43
        return $this->l10n->t('External file') . ': csv';
44
    }
45
46
    /**
47
     * @return int digit unique data source id
48
     */
49
    public function getId(): int
50
    {
51
        return 4;
52
    }
53
54
    /**
55
     * @return array available options of the datasoure
56
     */
57
    public function getTemplate(): array
58
    {
59
        $template = array();
60
        $template[] = ['id' => 'link', 'name' => $this->l10n->t('External URL'), 'placeholder' => 'url'];
61
        $template[] = ['id' => 'hasHeader', 'name' => $this->l10n->t('Header row'), 'placeholder' => 'true-' . $this->l10n->t('Yes').'/false-'.$this->l10n->t('No'), 'type' => 'tf'];
62
        $template[] = ['id' => 'offset', 'name' => $this->l10n->t('Ignore leading rows'), 'placeholder' => $this->l10n->t('Number of rows'), 'type' => 'number'];
63
        $template[] = ['id' => 'columns', 'name' => $this->l10n->t('Select columns'), 'placeholder' => $this->l10n->t('e.g. 1,2,4 or leave empty'), 'type' => 'columnPicker'];
64
        return $template;
65
    }
66
67
    /**
68
     * Read the Data
69
     * @param $option
70
     * @return array available options of the datasoure
71
     */
72
    public function readData($option): array
73
    {
74
        $url = htmlspecialchars_decode($option['link'], ENT_NOQUOTES);
75
        $ch = curl_init();
76
        if ($ch !== false) {
77
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
78
            curl_setopt($ch, CURLOPT_HEADER, false);
79
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
80
            curl_setopt($ch, CURLOPT_URL, $url);
81
            curl_setopt($ch, CURLOPT_REFERER, $url);
82
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
83
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
84
            $curlResult = curl_exec($ch);
85
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
86
            curl_close($ch);
87
        } else {
88
            $curlResult = '';
89
        }
90
91
        $rows = str_getcsv($curlResult, "\n");
0 ignored issues
show
Bug introduced by
It seems like $curlResult can also be of type true; however, parameter $string of str_getcsv() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
        $rows = str_getcsv(/** @scrutinizer ignore-type */ $curlResult, "\n");
Loading history...
92
93
        // remove x number of rows from the beginning
94
        if (isset($option['offset']) and is_numeric($option['offset'])) {
95
            $rows = array_slice($rows, $option['offset']);
96
        }
97
98
        $selectedColumns = array();
99
        if (isset($option['columns']) && strlen($option['columns']) > 0) {
100
            $selectedColumns = str_getcsv($option['columns'], ',');
101
        }
102
103
        // get the delimiter by reading the first row
104
        $delimiter = $this->detectDelimiter($rows[0]);
105
106
        // the first row will define the column headers, even if it is not a real header
107
        $header = str_getcsv($rows[0], $delimiter);
108
109
        // if the data has a real header, remove the first row
110
        if (!isset($option['hasHeader']) or $option['hasHeader'] !== 'false') {
111
            $rows = array_slice($rows, 1);
112
        }
113
114
        $data = array();
115
        if (count($selectedColumns) !== 0) {
116
            // if only a subset of columns or fixed column values are set, they are replaced here
117
            $header = $this->minimizeRow($selectedColumns, $header);
118
            foreach ($rows as $row) {
119
                $data[] = $this->minimizeRow($selectedColumns, str_getcsv($row, $delimiter));
120
            }
121
        } else {
122
            foreach ($rows as $row) {
123
                $data[] = str_getcsv($row, $delimiter);
124
            }
125
        }
126
        unset($rows);
127
        
128
        return [
129
            'header' => $header,
130
            'dimensions' => array_slice($header, 0, count($header) - 1),
131
            'data' => $data,
132
            'rawdata' => $curlResult,
133
            'error' => ($http_code>=200 && $http_code<300) ? 0 : 'HTTP response code: '.$http_code,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $http_code does not seem to be defined for all execution paths leading up to this point.
Loading history...
134
        ];
135
    }
136
137
    private function minimizeRow($selectedColumns, $row)
138
    {
139
        $rowMinimized = array();
140
        foreach ($selectedColumns as $selectedColumn) {
141
            if (is_numeric($selectedColumn)) {
142
                $rowMinimized[] = $row[$selectedColumn - 1];
143
            } else {
144
                // if columns contain replacement variables, they are processed here
145
                $rowMinimized[] = $this->VariableService->replaceDatasourceColumns($selectedColumn);
146
             }
147
        }
148
        return $rowMinimized;
149
    }
150
151
    private function detectDelimiter($data)
152
    {
153
        $delimiters = ["\t", ";", "|", ","];
154
        $data_2 = array();
155
        $delimiter = $delimiters[0];
156
        foreach ($delimiters as $d) {
157
            //$firstRow = str_getcsv($data, "\n")[0];
158
            $data_1 = str_getcsv($data, $d);
159
            if (sizeof($data_1) > sizeof($data_2)) {
160
                $delimiter = $d;
161
                $data_2 = $data_1;
162
            }
163
        }
164
        return $delimiter;
165
    }
166
}