File Coverage

blib/lib/Wikibase/API.pm
Criterion Covered Total %
statement 33 96 34.3
branch 0 24 0.0
condition 0 3 0.0
subroutine 11 18 61.1
pod 4 4 100.0
total 48 145 33.1


line stmt bran cond sub pod time code
1             package Wikibase::API;
2              
3 4     4   238777 use strict;
  4         36  
  4         177  
4 4     4   32 use warnings;
  4         14  
  4         125  
5              
6 4     4   1164 use Class::Utils qw(set_params);
  4         27704  
  4         153  
7 4     4   158 use Error::Pure qw(err);
  4         8  
  4         179  
8 4     4   1672 use JSON::XS qw(encode_json);
  4         10820  
  4         194  
9 4     4   2409 use MediaWiki::API;
  4         323446  
  4         168  
10 4     4   1889 use Unicode::UTF8 qw(decode_utf8);
  4         2041  
  4         288  
11 4     4   2570 use Wikibase::Datatype::Struct::Item;
  4         2443158  
  4         221  
12 4     4   2350 use Wikibase::Datatype::Struct::Lexeme;
  4         33698  
  4         223  
13 4     4   2139 use Wikibase::Datatype::Struct::Mediainfo;
  4         38326  
  4         196  
14 4     4   2258 use Wikibase::Datatype::Struct::Property;
  4         13553  
  4         4369  
15              
16             our $VERSION = 0.04;
17              
18             # Constructor.
19             sub new {
20 0     0 1   my ($class, @params) = @_;
21              
22             # Create object.
23 0           my $self = bless {}, $class;
24              
25             # MediaWiki::API object.
26 0           $self->{'mediawiki_api'} = MediaWiki::API->new;
27              
28             # MediaWiki site.
29 0           $self->{'mediawiki_site'} = 'test.wikidata.org';
30              
31             # Login name.
32 0           $self->{'login_name'} = undef;
33              
34             # Login password.
35 0           $self->{'login_password'} = undef;
36              
37             # Process parameters.
38 0           set_params($self, @params);
39              
40 0           $self->{'_mediawiki_base_uri'} = 'https://'.$self->{'mediawiki_site'};
41             # XXX Entity URI has http instead of https.
42 0 0         if ($self->{'mediawiki_site'} eq 'test.wikidata.org') {
43             # XXX test.wikidata.org has same entity url as Wikidata.
44 0           $self->{'_mediawiki_entity_uri'} = 'http://www.wikidata.org/entity/';
45             } else {
46 0           $self->{'_mediawiki_entity_uri'} = 'http://'.$self->{'mediawiki_site'}.'/entity/';
47             }
48              
49 0 0         if (ref $self->{'mediawiki_api'} ne 'MediaWiki::API') {
50 0           err "Parameter 'mediawiki_api' must be a 'MediaWiki::API' instance."
51             }
52             $self->{'mediawiki_api'}->{'config'}->{'api_url'}
53 0           = $self->{'_mediawiki_base_uri'}.'/w/api.php';
54              
55 0           $self->{'_init'} = 0;
56              
57 0           return $self;
58             }
59              
60             sub create_item {
61 0     0 1   my ($self, $item_obj) = @_;
62              
63 0           $self->_init;
64              
65             my $res = $self->{'mediawiki_api'}->api({
66             'action' => 'wbeditentity',
67             'new' => 'item',
68             'data' => $self->_obj2json($item_obj),
69 0           'token' => $self->{'_csrftoken'},
70             });
71 0           $self->_mediawiki_api_error($res, 'Cannot create item.');
72              
73 0           return $res;
74             }
75              
76             sub get_item {
77 0     0 1   my ($self, $id, $opts_hr) = @_;
78              
79 0           $self->_init;
80              
81 0           my $struct_hr = $self->get_item_raw($id, $opts_hr);
82 0 0         if (! exists $struct_hr->{'type'}) {
83 0           return;
84             }
85              
86 0           my $item_obj;
87 0 0         if ($struct_hr->{'type'} eq 'item') {
    0          
    0          
    0          
88 0           $item_obj = Wikibase::Datatype::Struct::Item::struct2obj($struct_hr);
89             } elsif ($struct_hr->{'type'} eq 'mediainfo') {
90 0           $item_obj = Wikibase::Datatype::Struct::Mediainfo::struct2obj($struct_hr);
91             } elsif ($struct_hr->{'type'} eq 'lexeme') {
92 0           $item_obj = Wikibase::Datatype::Struct::Lexeme::struct2obj($struct_hr);
93             } elsif ($struct_hr->{'type'} eq 'property') {
94 0           $item_obj = Wikibase::Datatype::Struct::Property::struct2obj($struct_hr);
95             } else {
96             err 'Unsupported type.',
97 0           'Type', $struct_hr->{'type'},
98             ;
99             }
100              
101 0           return $item_obj;
102             }
103              
104             sub get_item_raw {
105 0     0 1   my ($self, $id, $opts_hr) = @_;
106              
107 0           $self->_init;
108              
109             # TODO $opts_hr - Muzu vyfiltrovat jenom claims napr.
110              
111 0           my $res = $self->{'mediawiki_api'}->api({
112             'action' => 'wbgetentities',
113             'format' => 'json',
114             'ids' => $id,
115             });
116 0           $self->_mediawiki_api_error($res, 'Cannot get item.');
117              
118 0           my $struct_hr = $res->{'entities'}->{$id};
119              
120 0           return $struct_hr;
121             }
122              
123             sub _init {
124 0     0     my $self = shift;
125              
126 0 0         if ($self->{'_init'}) {
127 0           return;
128             }
129              
130             # Login.
131 0 0 0       if (defined $self->{'login_name'} && defined $self->{'login_password'}) {
132             my $login_ret = $self->{'mediawiki_api'}->login({
133             'lgname' => $self->{'login_name'},
134 0           'lgpassword' => $self->{'login_password'},
135             });
136 0           $self->_mediawiki_api_error($login_ret, 'Cannot login.');
137             }
138              
139             # Token.
140 0           my $token_hr = $self->{'mediawiki_api'}->api({
141             'action' => 'query',
142             'meta' => 'tokens',
143             });
144 0           $self->_mediawiki_api_error($token_hr, 'Cannot get token.');
145 0           $self->{'_csrftoken'} = $token_hr->{'query'}->{'tokens'}->{'csrftoken'};
146              
147             # Initialized.
148 0           $self->{'_init'} = 1;
149              
150 0           return;
151             }
152              
153             sub _obj2json {
154 0     0     my ($self, $item_obj) = @_;
155              
156 0 0         if (! defined $item_obj) {
157 0           return '{}';
158             } else {
159 0 0         if (! $item_obj->isa('Wikibase::Datatype::Item')) {
160 0           err "Bad data. Must be 'Wikibase::Datatype::Item' object.";
161             }
162             }
163              
164             my $struct_hr = Wikibase::Datatype::Struct::Item::obj2struct($item_obj,
165 0           $self->{'_mediawiki_entity_uri'});
166              
167 0           my $json = decode_utf8(JSON::XS->new->utf8->encode($struct_hr));
168              
169 0           return $json;
170             }
171              
172             sub _mediawiki_api_error {
173 0     0     my ($self, $res, $message) = @_;
174              
175 0 0         if (! defined $res) {
176             err $message,
177             'Error code' => $self->{'mediawiki_api'}->{'error'}->{'code'},
178 0           'Error details' => $self->{'mediawiki_api'}->{'error'}->{'details'},
179             ;
180             }
181              
182 0           return;
183             }
184              
185             1;
186              
187             __END__
188              
189             =pod
190              
191             =encoding utf8
192              
193             =head1 NAME
194              
195             Wikibase::API - Wikibase API class.
196              
197             =head1 SYNOPSIS
198              
199             use Wikibase::API;
200              
201             my $obj = Wikibase::API->new(%params);
202             my $res = $obj->create_item($item_obj);
203             my $item_obj = $obj->get_item($id);
204             my $struct_hr = $obj->get_item_raw($id);
205              
206             =head1 METHODS
207              
208             =head2 C<new>
209              
210             my $obj = Wikibase::API->new(%params);
211              
212             Constructor.
213              
214             Returns instance of object.
215              
216             =over 8
217              
218             =item * C<mediawiki_api>
219              
220             MediaWiki::API object.
221              
222             Default value is MediaWiki::API->new.
223              
224             =item * C<mediawiki_site>
225              
226             MediaWiki site.
227              
228             Default value is 'test.wikidata.org'.
229              
230             =item * C<login_name>
231              
232             Login name.
233              
234             Default value is undef.
235              
236             =item * C<login_password>
237              
238             Login password.
239              
240             Default value is undef.
241              
242             =back
243              
244             =head2 C<create_item>
245              
246             my $res = $obj->create_item($item_obj)
247              
248             Create item in system.
249             C<$item_obj> is Wikibase::Datatype::Item instance.
250              
251             Returns reference to hash like this:
252              
253             {
254             'entity' => {
255             ...
256             },
257             'success' => __STATUS_CODE__,
258             }
259              
260             =head2 C<get_item>
261              
262             my $item_obj = $obj->get_item($id);
263              
264             Get item from system.
265              
266             Returns Wikibase::Datatype::Item instance.
267              
268             =head2 C<get_item_raw>
269              
270             my $struct_hr = $obj->get_item_raw($id);
271              
272             Get item raw structure as Perl hash.
273              
274             Returns reference to hash.
275              
276             =head1 ERRORS
277              
278             new():
279             From Class::Utils::set_params():
280             Unknown parameter '%s'.
281             Cannot login.
282             Error code: %s
283             Error details: %s
284             Cannot get token.
285             Error code: %s
286             Error details: %s
287              
288             create_item():
289             Bad data. Must be 'Wikibase::Datatype::Item' object.
290              
291             =head1 EXAMPLE1
292              
293             =for comment filename=create_item_in_test_wikidata.pl
294              
295             use strict;
296             use warnings;
297              
298             use Data::Printer;
299             use Wikibase::API;
300             use Wikibase::Datatype::Item;
301              
302             # API object.
303             my $api = Wikibase::API->new;
304              
305             # Wikibase::Datatype::Item blank object.
306             my $item_obj = Wikibase::Datatype::Item->new;
307              
308             # Create item.
309             my $res = $api->create_item($item_obj);
310              
311             # Dump response structure.
312             p $res;
313              
314             # Output like:
315             # \ {
316             # entity {
317             # aliases {},
318             # claims {},
319             # descriptions {},
320             # id "Q213698",
321             # labels {},
322             # lastrevid 535146,
323             # sitelinks {},
324             # type "item"
325             # },
326             # success 1
327             # }
328              
329             =head1 EXAMPLE2
330              
331             =for comment filename=get_item_from_test_wikidata.pl
332              
333             use strict;
334             use warnings;
335              
336             use Data::Printer;
337             use Wikibase::API;
338              
339             if (@ARGV < 1) {
340             print STDERR "Usage: $0 id\n";
341             exit 1;
342             }
343             my $id = $ARGV[0];
344              
345             # API object.
346             my $api = Wikibase::API->new;
347              
348             # Get item.
349             my $item_obj = $api->get_item($id);
350              
351             # Dump response structure.
352             p $item_obj;
353              
354             # Output for Q213698 argument like:
355             # Wikibase::Datatype::Item {
356             # Parents Mo::Object
357             # public methods (9) : BUILD, can (UNIVERSAL), DOES (UNIVERSAL), err (Error::Pure), check_array_object (Mo::utils), check_number (Mo::utils), check_number_of_items (Mo::utils), isa (UNIVERSAL), VERSION (UNIVERSAL)
358             # private methods (1) : __ANON__ (Mo::is)
359             # internals: {
360             # aliases [],
361             # descriptions [],
362             # id "Q213698",
363             # labels [],
364             # lastrevid 535146,
365             # modified "2020-12-11T22:26:06Z",
366             # ns 0,
367             # page_id 304259,
368             # sitelinks [],
369             # statements [],
370             # title "Q213698"
371             # }
372             # }
373              
374             =head1 EXAMPLE3
375              
376             =for comment filename=get_item_raw_from_test_wikidata.pl
377              
378             use strict;
379             use warnings;
380              
381             use Data::Printer;
382             use Wikibase::API;
383              
384             if (@ARGV < 1) {
385             print STDERR "Usage: $0 id\n";
386             exit 1;
387             }
388             my $id = $ARGV[0];
389              
390             # API object.
391             my $api = Wikibase::API->new;
392              
393             # Get item.
394             my $struct_hr = $api->get_item_raw($id);
395              
396             # Dump response structure.
397             p $struct_hr;
398              
399             # Output for Q213698 argument like:
400             # {
401             # aliases {},
402             # claims {
403             # P623 [
404             # [0] {
405             # id "Q213698$89A385A8-2BE1-46CA-85FF-E0B53DEBC0F0",
406             # mainsnak {
407             # datatype "string",
408             # datavalue {
409             # type "string",
410             # value "101 Great Marques /Andrew Whyte." (dualvar: 101)
411             # },
412             # hash "db60f4054e0048355b75a07cd84f83398a84f515",
413             # property "P623",
414             # snaktype "value"
415             # },
416             # qualifiers {
417             # P446 [
418             # [0] {
419             # datatype "string",
420             # datavalue {
421             # type "string",
422             # value "a[1] c[1]"
423             # },
424             # hash "831cae40e488a0e8f4b06111ab3f1e1f8c42e79a" (dualvar: 831),
425             # property "P446",
426             # snaktype "value"
427             # }
428             # ],
429             # P624 [
430             # [0] {
431             # datatype "string",
432             # datavalue {
433             # type "string",
434             # value 1
435             # },
436             # hash "32eaf6cc04d6387b0925aea349bba4e35d2bc186" (dualvar: 32),
437             # property "P624",
438             # snaktype "value"
439             # }
440             # ],
441             # P625 [
442             # [0] {
443             # datatype "string",
444             # datavalue {
445             # type "string",
446             # value 0
447             # },
448             # hash "7b763330efc9d8269854747714d91ae0d0bc87a0" (dualvar: 7),
449             # property "P625",
450             # snaktype "value"
451             # }
452             # ],
453             # P626 [
454             # [0] {
455             # datatype "string",
456             # datavalue {
457             # type "string",
458             # value "101 Great Marques /" (dualvar: 101)
459             # },
460             # hash "0d2c3b012d13b9de1477bae831bd6d61a46e8c64",
461             # property "P626",
462             # snaktype "value"
463             # }
464             # ],
465             # P628 [
466             # [0] {
467             # datatype "string",
468             # datavalue {
469             # type "string",
470             # value "Andrew Whyte."
471             # },
472             # hash "a2c9c46ce7b17b13b197179fb0e5238965066211",
473             # property "P628",
474             # snaktype "value"
475             # }
476             # ]
477             # },
478             # qualifiers-order [
479             # [0] "P624",
480             # [1] "P626",
481             # [2] "P628",
482             # [3] "P446",
483             # [4] "P625"
484             # ],
485             # rank "normal",
486             # references [
487             # [0] {
488             # hash "98b2538ea26ec4da8e4aab27e74f1d832490a846" (dualvar: 98),
489             # snaks {
490             # P9 [
491             # [0] {
492             # datatype "wikibase-item",
493             # datavalue {
494             # type "wikibase-entityid",
495             # value {
496             # entity-type "item",
497             # id "Q1886",
498             # numeric-id 1886
499             # }
500             # },
501             # hash "271c3f13dd08a66f38eb2571d2f338e8b4b8074a" (dualvar: 271),
502             # property "P9",
503             # snaktype "value"
504             # }
505             # ],
506             # P21 [
507             # [0] {
508             # datatype "url",
509             # datavalue {
510             # type "string",
511             # value "http://lccn.loc.gov/87103973/marcxml"
512             # },
513             # hash "1e253d1dcb9867353bc71fc7c661cdc777e14885" (dualvar: 1e+253),
514             # property "P21",
515             # snaktype "value"
516             # }
517             # ]
518             # },
519             # snaks-order [
520             # [0] "P9",
521             # [1] "P21"
522             # ]
523             # }
524             # ],
525             # type "statement"
526             # }
527             # ]
528             # },
529             # descriptions {
530             # en {
531             # language "en",
532             # value 87103973
533             # },
534             # it {
535             # language "it",
536             # value 87103973
537             # }
538             # },
539             # id "Q213698",
540             # labels {
541             # en {
542             # language "en",
543             # value "101 Great Marques /" (dualvar: 101)
544             # },
545             # it {
546             # language "it",
547             # value "101 Great Marques /" (dualvar: 101)
548             # }
549             # },
550             # lastrevid 538778,
551             # modified "2021-03-20T14:35:50Z" (dualvar: 2021),
552             # ns 0,
553             # pageid 304259,
554             # sitelinks {},
555             # title "Q213698",
556             # type "item"
557             # }
558              
559             =head1 DEPENDENCIES
560              
561             L<Class::Utils>,
562             L<Error::Pure>,
563             L<JSON::XS>,
564             L<MediaWiki::API>,
565             L<Unicode::UTF8>,
566             L<Wikibase::Datatype::Item>.
567              
568             =head1 SEE ALSO
569              
570             =over
571              
572             =item L<Wikibase::Datatype>
573              
574             Wikibase datatypes.
575              
576             =item L<Wikibase::Datatype::Struct>
577              
578             Wikibase structure serialization.
579              
580             =back
581              
582             =head1 REPOSITORY
583              
584             L<https://github.com/michal-josef-spacek/Wikibase-API>
585              
586             =head1 AUTHOR
587              
588             Michal Josef Špaček L<mailto:skim@cpan.org>
589              
590             L<http://skim.cz>
591              
592             =head1 LICENSE AND COPYRIGHT
593              
594             © 2020-2023 Michal Josef Špaček
595              
596             BSD 2-Clause License
597              
598             =head1 VERSION
599              
600             0.04
601              
602             =cut