Completed
Push — master ( c5d7d1...4cd350 )
by Enrico
12:38 queued 07:39
created

GoogleMapQueryCommand::execute()   F

Complexity

Conditions 32
Paths > 20000

Size

Total Lines 182
Code Lines 111

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 1056

Importance

Changes 0
Metric Value
dl 0
loc 182
ccs 0
cts 18
cp 0
rs 2
c 0
b 0
f 0
cc 32
eloc 111
nc 788836
nop 2
crap 1056

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
namespace BOTK\Command;
3
4
use Symfony\Component\Console\Command\Command;
5
use Symfony\Component\Console\Input\InputInterface;
6
use Symfony\Component\Console\Input\InputOption;
7
use Symfony\Component\Console\Output\OutputInterface;
8
use Symfony\Component\Console\Question\Question;
9
use SKAgarwal\GoogleApi\PlacesApi;
10
use BOTK\FactsFactory;
11
12
class GoogleMapQueryCommand extends Command
13
{    
0 ignored issues
show
Coding Style introduced by
The opening class brace should be on a newline by itself.
Loading history...
14
    protected function configure()
15
    {
16
        $this
17
        ->setName('google:places:reasoner')
18
        ->setDescription('Discover information about a local business using Google places APIs.')
19
        ->setHelp('This command search a name in google places returning a ttl file according botk Language profile....')
20
        ->addOption('key','k',  InputOption::VALUE_REQUIRED, 
21
            'the mandatory google place api key (see https://developers.google.com/places/web-service/get-api-key)'
22
        )
23
        ->addOption('namespace','u',  InputOption::VALUE_REQUIRED,
24
            'the namespace for created URI',
25
            'http://linkeddata.center/resource/'
26
        )
27
        ->addOption('delay','d',  InputOption::VALUE_REQUIRED,
28
            'delay each call of a fixed amount of seconds',
29
            0
30
        )
31
        ->addOption('skip','s', InputOption::VALUE_REQUIRED,
32
            'number of INPUT lines to skip',
33
            1
34
        )
35
        ->addOption('resilience','r', InputOption::VALUE_REQUIRED,
36
            'max number of errors tolerated before aborting',
37
            10
38
        )
39
        ->addOption('fields','f', InputOption::VALUE_REQUIRED,
40
            'detalis level required (none|contact)',
41
            'contact'
42
        )
43
        ->addOption('type','t', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
44
            'additional RDF type as uri',
45
            array('http://schema.org/Place')
46
        )
47
        ->addOption('assert','a', InputOption::VALUE_REQUIRED,
48
            'asserted link predicate (sameAs|similarTo)',
49
            'similarTo'
50
        )
51
        ->addOption('limit','l', InputOption::VALUE_REQUIRED,
52
            'max number of calls to google APIs',
53
            4000
54
        );
55
    }
56
    
57
    
58
    protected function execute(InputInterface $input, OutputInterface $output)
59
    {    
60
        //cache input parameters
61
        $uriNameSpace = $input->getOption('namespace');
62
        $limit = $input->getOption('limit');
63
        $sleepTime = $input->getOption('delay');
64
        $detailLevel = $input->getOption('fields');
65
        $resilience = $input->getOption('resilience');
66
        $types = $input->getOption('type');
67
        $similarityPredicate = $input->getOption('assert');
68
        
69
        if( !($key = $input->getOption('key'))){
70
            
71
            $helper = $this->getHelper('question');
72
            $question = new Question('Please enter your google place api key: ');
73
            $question->setValidator(function ($value) {
74
                if (trim($value) == '') {
75
                    throw new \Exception('The key cannot be empty');
76
                }
77
                
78
                return $value;
79
            });
80
            $question->setHidden(true);
81
            $question->setMaxAttempts(20);
82
            
83
            $key = $helper->ask($input, $output, $question);
84
        }
85
        
86
        $googlePlaces = new PlacesApi($key);
87
        $factsFactory = new FactsFactory( array(
88
            'model'			=> 'LocalBusiness',
89
            'modelOptions'		=> array(
90
                // override the default lowercase filter for id because placeId is case sensitive
91
                'id' => array('filter'=> FILTER_DEFAULT)
92
            )
93
        ));
94
        
95
        // print turtle prefixes
96
        echo $factsFactory->generateLinkedDataHeader();
97
98
        $lineCount=$callErrorCount = $consecutiveErrorsCount = $callCount = 0; 
99
        
100
        // skip input headers
101
        for ($i = 0; $i < $input->getOption('skip'); $i++) {
102
            $lineCount++;
103
            $output->writeln("<info># Ignored header $lineCount: ". trim(fgets(STDIN)) . '</info>'); 
104
        }
105
        
106
        // main input loop
107
        while( ($rawData= fgetcsv(STDIN)) && ($callCount <$limit)  ){
108
            $lineCount++;
109
            if(!is_array($rawData) || (count($rawData)!=2)) { 
110
                $output->writeln("<error># Ignored invalid row at line $lineCount.</error>");
111
                continue; 
112
            }
113
            list($uri, $query) = $rawData;
114
            
115
                      
116
            //--------------------------------------------------------------------------------
117
            // call google place textSearch api, tolerating some errors.
118
            //--------------------------------------------------------------------------------
119
            try {
120
                $searchResultsCollection=$googlePlaces->textSearch($query, array('region'=>'IT'));
121
                $consecutiveErrorsCount=0;
122
                $callCount++;
123
            } catch (\Exception $e) {
124
                $consecutiveErrorsCount++;$callErrorCount++;
125
                if( $consecutiveErrorsCount > $resilience ){
126
                    throw $e;
127
                }
128
                $messageString = trim(preg_replace('/\s+/', ' ', $e->getMessage()));
129
                $output->writeln("<error># Ignored Search Api ERROR ($consecutiveErrorsCount): $messageString</error>");
130
                continue;
131
            }
132
            
133
            // skip empty results
134
            if ($googlePlaces->getStatus()==='ZERO_RESULTS'){   
135
                $output->writeln("<info># no results for '$query'.</info>");
136
                continue;
137
            }
138
            
139
            $output->writeln("<info># discovered data for '$query'.</info>");
140
            // factualize textSearch results
141
            $result =$searchResultsCollection['results']->first();
142
            $placeId = $result['place_id'];
143
            $data['id'] = $placeId;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
144
            $data['uri'] = $uriNameSpace . $placeId;
0 ignored issues
show
Bug introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
145
            $data['businessType'] = $types;
146
            $data[$similarityPredicate] = $uri; 
147
            
148
            if( isset($result['geometry']['location'])) {
149
                $data['lat'] = $result['geometry']['location']['lat'];
150
                $data['long'] = $result['geometry']['location']['lng'];
151
            }
152
            if( isset($result['formatted_address'])) {
153
                $data['addressDescription'] = $result['formatted_address'];
154
            }
155
            if( isset($result['name'])) {
156
                $data['businessName'] = $result['name'];
157
            }
158
            if( isset($result['types'])) {
159
                $data['disambiguatingDescription'] = $result['types'];
160
            } 
161
            
162
163
            //--------------------------------------------------------------------------------
164
            // call google place details api, tolerating some errors.
165
            //--------------------------------------------------------------------------------            
166
            if ($detailLevel==='contact') {
167
                try {
168
                    $details=$googlePlaces->placeDetails($placeId, array('region'=>'IT'));
169
                    $consecutiveErrorsCount=0;
170
                    $callCount++;
171
                } catch (\Exception $e) {
172
                    $consecutiveErrorsCount++;$callErrorCount++;
173
                    if( $consecutiveErrorsCount > $resilience){
174
                        throw $e;
175
                    }
176
                    $messageString = trim(preg_replace('/\s+/', ' ', $e->getMessage()));
177
                    $output->writeln("<error># Ignored Details Api ERROR ($consecutiveErrorsCount): $messageString</error>");
178
                }
179
                
180
                // skip empty results
181
                if ('OK' === $googlePlaces->getStatus()){               
182
                    // factualize placeDetails results
183
                    $result =$details['result'];
184
                    if( isset($result['address_components'][1]['short_name']) ) {
185
                        $data['streetAddress'] = $result['address_components'][1]['short_name'];
186
                    }
187
                    if( isset($result['address_components'][0]['short_name']) ) {
188
                        $data['streetAddress'] .= ', ' . $result['address_components'][0]['short_name'];
189
                    }
190
                    if( isset($result['address_components'][3]['short_name']) ) {
191
                        $data['addressLocality'] = $result['address_components'][3]['short_name'];
192
                    }
193
                    if( isset($result['address_components'][0]['short_name']) ) {
194
                        $data['addressRegion'] = $result['address_components'][4]['short_name'];
195
                    }
196
                    if( isset($result['address_components'][5]['short_name']) ) {
197
                        $data['addressRegioneIstat'] = $result['address_components'][5]['short_name'];
198
                    }
199
                    if( isset($result['address_components'][7]['short_name']) ) {
200
                        $data['postalCode'] = $result['address_components'][7]['short_name'];
201
                    }
202
                    if( isset($result['formatted_phone_number']) ) {
203
                        $data['telephone'] = $result['formatted_phone_number'];
204
                    }
205
                    if( isset($result['website']) ) {
206
                        $data['page'] = $result['website'];
207
                    }
208
                    if( isset($result['url']) ) {
209
                        $data['hasMap'] = $result['url'];
210
                    }
211
                } else {                  
212
                    $output->writeln("<info># no details for place id '$placeId' details</info>");
213
                }
214
            }
215
            
216
            try {
217
                $facts =$factsFactory->factualize($data);
218
                echo $facts->asTurtleFragment(), "\n";
219
                $droppedFields = $facts->getDroppedFields();
220
                if(!empty($droppedFields)) {
221
                    $output->writeln("<error># Dropped ".implode(", ", $droppedFields).'</error>');
222
                    $this->factsFactory->addToCounter('error');
0 ignored issues
show
Bug introduced by
The property factsFactory does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
223
                }
224
            } catch (\BOTK\Exception\Warning $e) {
225
                $output->writeln("<comment># ".$e->getMessage().'</comment>');
226
            } 
227
            
228
            
229
            sleep($sleepTime);
230
        }
231
        
232
        if ($callCount >= $limit && $placeId) {
0 ignored issues
show
Bug introduced by
The variable $placeId does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
233
            $output->writeln("<comment># Api call limit reached ($callCount).</comment>");
234
        }
235
        
236
        // prints provenances and other metadata
237
        echo $factsFactory->generateLinkedDataFooter();
238
        $output->writeln("<info># Called $callCount APIs, $callErrorCount errors.</info>");
239
    }
240
}