Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
88.71% covered (warning)
88.71%
55 / 62
88.89% covered (warning)
88.89%
40 / 45
35.14% covered (danger)
35.14%
13 / 37
70.00% covered (warning)
70.00%
7 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ContributorCollection
88.71% covered (warning)
88.71%
55 / 62
88.89% covered (warning)
88.89%
40 / 45
35.14% covered (danger)
35.14%
13 / 37
70.00% covered (warning)
70.00%
7 / 10
210.49
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 curlErrorNumber
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 curlErrorInfo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPreferred
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
6 / 6
25.00% covered (danger)
25.00%
1 / 4
100.00% covered (success)
100.00%
1 / 1
6.80
 getIterator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findBestContributorByAnalysisType
83.33% covered (warning)
83.33%
10 / 12
88.24% covered (warning)
88.24%
15 / 17
17.65% covered (danger)
17.65%
3 / 17
0.00% covered (danger)
0.00%
0 / 1
43.75
 fetch
95.00% covered (success)
95.00%
19 / 20
83.33% covered (warning)
83.33%
5 / 6
50.00% covered (danger)
50.00%
2 / 4
0.00% covered (danger)
0.00%
0 / 1
6.00
 keepOnlyRowsWithMdpSet
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findContributors
77.78% covered (warning)
77.78%
14 / 18
80.00% covered (warning)
80.00%
8 / 10
16.67% covered (danger)
16.67%
1 / 6
0.00% covered (danger)
0.00%
0 / 1
19.47
 isCurlCloseSupported
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Dbmi\Webservice\Contributor;
3
4use ArrayIterator;
5use IteratorAggregate;
6use Traversable;
7
8use Dbmi\Webservice\Quake\QuakeId;
9
10/**
11 * Get contributors
12 */
13class ContributorCollection implements IteratorAggregate{
14    private const templateUrl = 'https://emidius.mi.ingv.it/services/macroseismic/query?eventid=%s&includeallmdpsets=true&format=textmacro';
15    private const minValidResponseLines = 2;
16
17    private const curlCloceLatestPhpVersion = 7;
18
19    private QuakeId $quakeId;
20    private array $details = array();
21
22    private array $contributors = array();
23
24    private int $curlErrNum = 0;
25    private string $curlErrInfo = '';
26
27
28    public function __construct(QuakeId $quakeId){
29        $this->quakeId = $quakeId;
30    }
31
32    public function curlErrorNumber(){ return $this->curlErrNum; }
33    public function curlErrorInfo(){ return $this->curlErrInfo; }
34
35    /** 
36     * string $contributor set already choose contributor
37     * 
38     * @return bool TRUE contributor found, false otherwise
39     */
40    public function setPreferred(ContributorData $contributor):bool{ 
41        $found = false;
42        foreach($this->contributors as $elem){
43            $elem->setPreferred( $elem->details()->name() == $contributor->name() );
44            if( ! $found )
45                $found = ($elem->details()->name() == $contributor->name());
46        }
47        return $found;
48    }
49
50    /**
51     * get all contributors
52     * 
53     * @return array array of strings with the found contributors
54     */
55    public function getIterator():Traversable{ return new ArrayIterator($this->contributors); }
56
57    /**
58     * get the "best" contributor based an array of analysis type
59     * 
60     * @param array $analysisName find the contributor with the analysis preferred based 
61     *                 on array of names, sorted by preference (for example: array('MCS', 'EMS', ...)
62     * @return Dbmi\Webservice\Contributor\Contributor the Contributor found or null
63     */
64    public function findBestContributorByAnalysisType(array $analysisNames):?Contributor {
65        if ( empty($analysisNames) )
66            return null;
67
68        $bestContributor = null;
69        foreach($analysisNames as $name){
70            foreach($this->contributors as $contributor){
71                //same analysis and isPreferred -> it's the best
72                if( $contributor->analisysExists($name) && $contributor->isPreferred() )
73                    return $contributor;
74
75                //same analysis -> i can take it but i loop for a better solution (maybe preferred is later...)
76                if( $contributor->analisysExists($name) )
77                    $bestContributor = $contributor;
78            }
79
80            //if a contributor is found, i'll get it before instead search it in other analysis
81            if( ! is_null($bestContributor) )
82                return $bestContributor;
83        }
84
85        return $bestContributor;
86    }
87    
88    /**
89     * find contributor based on quakeId
90     *
91     * @return int number of studies found
92     */
93    public function fetch():int{
94        $curlSession = curl_init();
95        curl_setopt_array($curlSession, array(
96            CURLOPT_URL         => sprintf(self::templateUrl, $this->quakeId->id()),
97            CURLOPT_HEADER         => false,
98            CURLOPT_CUSTOMREQUEST    => 'GET',
99            CURLOPT_RETURNTRANSFER    => true,
100            CURLOPT_FAILONERROR     => true
101            )
102        );
103        $downloadedData = curl_exec($curlSession);
104
105        $this->curlErrNum = curl_errno($curlSession);
106        $this->curlErrInfo = curl_error($curlSession);
107        if ( $this->isCurlCloseSupported() )
108            curl_close($curlSession);
109
110        if(CURLE_OK != $this->curlErrNum){
111            // @codeCoverageIgnoreStart
112            error_log(sprintf("[%s] [CurlErr: %d] %s", __METHOD__, $this->curlErrNum, $this->curlErrInfo));
113            return -1;
114            // @codeCoverageIgnoreEnd
115        }
116
117        $rows = preg_split("/\n/", trim($downloadedData));
118
119        if( count($rows) < self::minValidResponseLines ){ //header+data
120            error_log(sprintf("[%s] Invalid contributors response, see below\n'%s'", __METHOD__, $downloadedData));
121            return -2;
122        }    
123
124        return $this->findContributors($downloadedData);
125    }
126
127    /** 
128     * filter rows with no mdpset    
129     */
130    private function keepOnlyRowsWithMdpSet($row){ return preg_match('@.*mdpset/(.*)/\d*@', $row); }
131
132    /**
133     * divide found contributors by intensity-type of study
134     */
135    private function findContributors($downloadedData):int{
136        $originaldataArray = preg_split("/\n/", $downloadedData);
137
138        $dataArray = array_filter($originaldataArray, array($this, 'keepOnlyRowsWithMdpSet'));
139        if(0 == count($dataArray)){
140            error_log(sprintf("[%s] No contributor lines with mdpset data\n'%s'", __METHOD__, $downloadedData));
141            return 0;
142        }
143
144        //default/dbmiPreferred contributor should be in the first line
145        $firstLine = true;
146        foreach($dataArray as $data){
147            list($eventId, $mdpsetId, $originTime, $region, $mdpCount, $maxIntensity, $macroseismicScale) = str_getcsv($data, '|', "\"", "\\");
148            if( ! isset($macroseismicScale) ){
149                error_log(sprintf("[%s] No macroseismic scale found at index[6]\n'%s'", __METHOD__, $data));
150                continue;
151            }
152
153            preg_match('@.*mdpset/(.*)/\d*@', $mdpsetId, $matchArray);
154            $contributorName = $matchArray[1];
155
156            if( ! array_key_exists($contributorName, $this->contributors) )
157                $this->contributors[ $contributorName ] = new Contributor( new ContributorData($contributorName, $mdpsetId), $this->quakeId, $firstLine );
158
159            $this->contributors[$contributorName]->addAnalysis($macroseismicScale);
160            $firstLine = false;
161        }
162
163        return count($this->contributors);
164    }
165
166    /**
167     * Check if curl_close is still supported
168     * @return bool true is supported, false otherwise
169     */
170    private function isCurlCloseSupported():bool{ return PHP_MAJOR_VERSION <= self::curlCloceLatestPhpVersion; }
171}
172
173?>
174