Passed
Push — master ( 62782a...a28cef )
by Marcel
04:19
created

ExternalCsv::readData()   C

Complexity

Conditions 13
Paths 256

Size

Total Lines 63
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 40
nc 256
nop 1
dl 0
loc 63
rs 5.0833
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Analytics
4
 *
5
 * SPDX-FileCopyrightText: 2019-2022 Marcel Scherello
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\Analytics\Datasource;
10
11
use OCP\IL10N;
0 ignored issues
show
Bug introduced by
The type OCP\IL10N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Psr\Log\LoggerInterface;
0 ignored issues
show
Bug introduced by
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use OCA\Analytics\Service\VariableService;
14
15
class ExternalCsv implements IDatasource
16
{
17
    private $logger;
18
    private $userId;
19
    private $l10n;
20
    private $VariableService;
21
22
    public function __construct(
23
        $userId,
24
        IL10N $l10n,
25
        LoggerInterface $logger,
26
        VariableService $VariableService
27
    )
28
    {
29
        $this->userId = $userId;
30
        $this->l10n = $l10n;
31
        $this->logger = $logger;
32
        $this->VariableService = $VariableService;
33
    }
34
35
    /**
36
     * @return string Display Name of the datasource
37
     */
38
    public function getName(): string
39
    {
40
        return $this->l10n->t('External') . ': csv';
41
    }
42
43
    /**
44
     * @return int digit unique data source id
45
     */
46
    public function getId(): int
47
    {
48
        return 4;
49
    }
50
51
    /**
52
     * @return array available options of the datasoure
53
     */
54
    public function getTemplate(): array
55
    {
56
        $template = array();
57
        $template[] = ['id' => 'link', 'name' => $this->l10n->t('External URL'), 'placeholder' => 'url'];
58
        $template[] = ['id' => 'hasHeader', 'name' => $this->l10n->t('Header row'), 'placeholder' => 'true-' . $this->l10n->t('Yes').'/false-'.$this->l10n->t('No'), 'type' => 'tf'];
59
        $template[] = ['id' => 'offset', 'name' => $this->l10n->t('Ignore leading rows'), 'placeholder' => $this->l10n->t('Number of rows'), 'type' => 'number'];
60
        $template[] = ['id' => 'columns', 'name' => $this->l10n->t('Select columns'), 'placeholder' => $this->l10n->t('e.g. 1,2,4 or leave empty'), 'type' => 'columnPicker'];
61
        return $template;
62
    }
63
64
    /**
65
     * Read the Data
66
     * @param $option
67
     * @return array available options of the datasoure
68
     */
69
    public function readData($option): array
70
    {
71
        $url = htmlspecialchars_decode($option['link'], ENT_NOQUOTES);
72
        $ch = curl_init();
73
        if ($ch !== false) {
74
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
75
            curl_setopt($ch, CURLOPT_HEADER, false);
76
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
77
            curl_setopt($ch, CURLOPT_URL, $url);
78
            curl_setopt($ch, CURLOPT_REFERER, $url);
79
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
80
            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');
81
            $curlResult = curl_exec($ch);
82
            $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
83
            curl_close($ch);
84
        } else {
85
            $curlResult = '';
86
        }
87
88
        $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

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