Passed
Push — master ( 5eccc7...1faa52 )
by Dispositif
02:45
created

WikidataAdapter   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Test Coverage

Coverage 70.83%

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 64
dl 0
loc 140
ccs 34
cts 48
cp 0.7083
rs 10
c 6
b 0
f 0
wmc 20

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getDataByInfos() 0 14 4
A ISNIvalide() 0 3 2
A __construct() 0 7 2
B sparqlRequest() 0 34 8
A findArticleByISBN13() 0 25 2
A searchByISNI() 0 21 2
1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application (@github)
4
 * 2019/2020 © Philippe M. <[email protected]>
5
 * For the full copyright and MIT license information, please view the license file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Infrastructure;
11
12
use DomainException;
13
use Exception;
14
use GuzzleHttp\Client;
15
use Normalizer;
16
17
/**
18
 * dirty scratch WikiData read requests
19
 * Class WikidataAdapter
20
 *
21
 * @package App\Infrastructure
22
 */
23
class WikidataAdapter
24
{
25
    private $client;
26
27 2
    public function __construct(?Client $client = null)
28
    {
29 2
        if ($client === null) {
30
            // lazy dependency factory :)
31
            $this->client = new Client(['timeout' => 60, 'headers' => ['User-Agent' => getenv('USER_AGENT')]]);
32
        } else {
33 2
            $this->client = $client;
34
        }
35 2
    }
36
37
    public function getDataByInfos(?array $infos)
38
    {
39
        $res = [];
40
        if (isset($infos['ISNIAuteur1'])) {
41
            $res = $this->searchByISNI($infos['ISNIAuteur1']);
42
        }
43
        if (isset($infos['isbn'])) {
44
            if(!empty($res)) {
45
                sleep(2);
46
            }
47
            $res = array_merge($res ?? [], $this->findArticleByISBN13($infos['isbn']));
48
        }
49
50
        return $res ?? [];
51
    }
52
53 1
    public function findArticleByISBN13(string $isbn): ?array
54
    {
55
        // strip ISBN formating
56 1
        $isbn = preg_replace('#[^0-9X]#', '', $isbn);
57 1
        if (strlen($isbn) !== 13) {
58
            throw new DomainException('ISBN-13 format error');
59
        }
60
61 1
        $sparql = sprintf(
62 1
            'select ?work ?workLabel ?articleBook ?edition ?isbn
63
WHERE {
64
    ?work wdt:P31 wd:Q47461344 ; # instance of written work
65
        wdt:P747 ?edition . # has edition (P747)
66
    ?edition wdt:P212 $isbn . # ISBN-13 (P212)
67
    FILTER(REGEX(REPLACE(?isbn,"-",""), "%s", "i")). # strip ISBN formating
68
    ?articleBook schema:about ?work ;
69
    		schema:isPartOf <https://fr.wikipedia.org/> # frwiki sitelink
70
    SERVICE wikibase:label {
71
        bd:serviceParam wikibase:language "fr" .
72
   }
73
}',
74 1
            $isbn
75
        );
76
77 1
        return $this->sparqlRequest($sparql);
78
    }
79
80
    /**
81
     * Get WD item, sitelink, VIAF from search by ISNI (author)
82
     *
83
     * @param string $isni
84
     *
85
     * @return array|null
86
     * @throws Exception
87
     */
88 1
    public function searchByISNI(string $isni): ?array
89
    {
90 1
        if (!$this->ISNIvalide($isni)) {
91 1
            new Exception('Invalid format for ISNI');
92
        }
93
94 1
        $sparql = sprintf(
95 1
            'SELECT distinct ?item ?itemLabel ?articleAuthor ?isni ?viaf WHERE {
96
  ?item wdt:P213 "%s" .
97
  ?item wdt:P213 ?isni.
98
  ?item wdt:P214 ?viaf.
99
  ?articleAuthor schema:about ?item ;
100
		schema:isPartOf <https://fr.wikipedia.org/>
101
  SERVICE wikibase:label {
102
    bd:serviceParam wikibase:language "fr" .
103
   }
104
}',
105 1
            $isni
106
        );
107
108 1
        return $this->sparqlRequest($sparql);
109
    }
110
111
    /**
112
     * @param string $sparql
113
     *
114
     * @return array|null
115
     * @throws Exception
116
     */
117 2
    private function sparqlRequest(string $sparql): ?array
118
    {
119 2
        $url = 'https://query.wikidata.org/bigdata/namespace/wdq/sparql?'.http_build_query(
120
                [
121 2
                    'format' => 'json',
122 2
                    'query' => $sparql, // rawurlencode()
123
                ]
124
            );
125
126
127
        // todo : catch + return null ?
128 2
        $response = $this->client->get($url);
129
130 2
        if (200 !== $response->getStatusCode()) {
131
            throw new Exception('response error '.$response->getStatusCode().' '.$response->getReasonPhrase());
132
        }
133 2
        $json = $response->getBody()->getContents();
134
135 2
        if (empty($json)) {
136
            return null;
137
        }
138 2
        $json = Normalizer::normalize($json);
139
140 2
        $array = json_decode($json, true) ?? null;
141
142
        // return first result only
143 2
        if ($array && isset($array['results']) && isset($array['results'])
144 2
            && isset($array['results']['bindings'])
145 2
            && count($array['results']['bindings']) === 1
146
        ) {
147 2
            return $array['results']['bindings'][0];
148
        }
149
150
        return null;
151
    }
152
153
    /**
154
     * todo move
155
     *
156
     * @param string $isni
157
     *
158
     * @return bool
159
     */
160 1
    private function ISNIvalide(string $isni): bool
161
    {
162 1
        return (!preg_match('#^0000(000[0-4])([0-9]{4})([0-9]{3}[0-9X])$#', $isni)) ? false : true;
163
    }
164
}
165