@@ -, +, @@ - "Wikidata is a free and open knowledge base that can be read and edited by both humans and machines. Wikidata acts as central storage for the structured data of its Wikimedia sister projects including Wikipedia, Wikivoyage, Wikisource, and others." - Bnf is the french national library - checks if there is an ark - try to grab data about the work or expression - get the author id - try to grab data about the author - display what you have in a div in the opac - there is a button to display some debug things --- Koha/Explore/Bnf.pm | 73 +++++++ Koha/Explore/Sparql.pm | 89 ++++++++ Koha/Explore/Wikidata.pm | 224 +++++++++++++++++++++ Koha/Template/Plugin/Explore.pm | 54 +++++ .../bootstrap/en/includes/explore/bnf.inc | 96 +++++++++ .../bootstrap/en/includes/explore/wikidata.inc | 122 +++++++++++ .../opac-tmpl/bootstrap/en/modules/opac-detail.tt | 10 + opac/opac-detail.pl | 8 + t/Koha_Template_Plugin_Explore.t | 37 ++++ 9 files changed, 713 insertions(+) create mode 100644 Koha/Explore/Bnf.pm create mode 100644 Koha/Explore/Sparql.pm create mode 100644 Koha/Explore/Wikidata.pm create mode 100644 Koha/Template/Plugin/Explore.pm create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/includes/explore/bnf.inc create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/includes/explore/wikidata.inc create mode 100644 t/Koha_Template_Plugin_Explore.t --- a/Koha/Explore/Bnf.pm +++ a/Koha/Explore/Bnf.pm @@ -0,0 +1,73 @@ +package Koha::Explore::Bnf; + +use C4::Biblio; +use Koha::Explore::Sparql; + +=head1 NAME + +Koha::Explore::Bnf + +=head1 SYNOPSIS + + use Koha::Explore::Bnf; + +=head1 FUNCTIONS + +=cut + +use Modern::Perl; +use RDF::Query::Client; + +=head2 _execute_sparql_query + Execute sparql query on Sparql endpoint. +=cut + +my $endpoint = "http://data.bnf.fr/sparql"; + +sub GetDataForSubject { + my ($subject) = @_; +} + +=head2 GetDataForAuthor + Execute sparql query on Sparql endpoint. +=cut + +sub GetDataForAuthor { + my ($ark) = @_; + $ark =~ s/catalogue/data/g; + + # Work Ark : I must go and searhc the author + my %workData = Koha::Explore::Sparql::GetPredicatesAndObjectsForSubject ("<".$ark.">",$endpoint); + my $authorArk = "<".$workData{'http://purl.org/dc/terms/creator'}[0].">"; + my %data = Koha::Explore::Sparql::GetPredicatesAndObjectsForSubject ($authorArk,$endpoint); + return \%data; +} + +=head2 GetDataForBiblio + Execute sparql query on Sparql endpoint. +=cut + +sub GetDataForBiblio { + my ($ark) = @_; + $ark =~ s/catalogue/data/g; + my %data = Koha::Explore::Sparql::GetPredicatesAndObjectsForSubject ("<".$ark.">",$endpoint); + return \%data; +} + +sub _llog { + my ($var,$a,$b) = @_; + use Data::Dumper; + $a = "" if !defined $a; + $b = "" if !defined $b; + warn Dumper($a . '##############################################'); + warn Dumper($var); + warn Dumper($b . '##############################################'); +} + +=head1 AUTHOR +Claire Hernandez, Eclaire.hernandez@biblibre.comE +=cut + +1; + +__END__ --- a/Koha/Explore/Sparql.pm +++ a/Koha/Explore/Sparql.pm @@ -0,0 +1,89 @@ +package Koha::Explore::Sparql; + +use C4::Biblio; + +=head1 NAME + +Koha::Explore::Sparql + + +=head1 SYNOPSIS + + use Koha::Explore::Sparql; + +=head1 FUNCTIONS + +=cut + +use Modern::Perl; +use RDF::Query::Client; + +=head2 Execute + Execute sparql query on Sparql endpoint. +=cut + +sub Execute { + my ($sparql,$endpoint) = @_; + my $query = RDF::Query::Client->new($sparql); + my $res = $query->execute($endpoint); + _llog($query->error,"ERROR") if (defined $query->error) ; + return $res; +} + + +sub GetPredicatesAndObjectsForSubject { + my ($subject,$endpoint,$query) = @_; + + my $sparql; + if (!defined $query) { + $sparql = 'SELECT DISTINCT ?p ?o WHERE { '.$subject.' ?p ?o . } '; + } else { + $sparql = $query; + } + _llog ($sparql,"sparql>","next) { + my $object; + my $predicate; + if ($row->{o}->type eq "URI") { + $object= $row->{o}->uri_value; + } else { + $object = $row->{o}[0]; + } + if ($row->{p}->type eq "URI") { + $predicate = $row->{p}->uri_value; + } else { + $predicate = $row->{p}[0]; + } + if (exists $predicates{$predicate}) { + push @{$predicates{$predicate}}, $object; + } else { + $predicates{$predicate} = [$object]; + } + } + _llog (\%predicates,"pre>",">>'); + warn Dumper($var); + warn Dumper($b . '<<<'); +} + +=head1 AUTHOR +Claire Hernandez, Eclaire.hernandez@biblibre.comE +=cut + +1; + +__END__ --- a/Koha/Explore/Wikidata.pm +++ a/Koha/Explore/Wikidata.pm @@ -0,0 +1,224 @@ +package Koha::Explore::Wikidata; + +use C4::Biblio; + +=head1 NAME + +Koha::Explore::Wikidata + + +=head1 SYNOPSIS + + use Koha::Explore::Wikidata; + +=head1 FUNCTIONS + +=cut + +use Modern::Perl; +use Koha::Explore::Sparql; + +my $endpoint = "https://query.wikidata.org/bigdata/namespace/wdq/sparql"; + +=head2 GetDataForAuthor + Execute sparql query on Sparql endpoint. +=cut +sub GetDataForAuthor { + my ($wdid) = @_; + my $id = _get_wd_id ($wdid); + + my $sparql = ' + SELECT DISTINCT ?p ?o + WHERE { + { + wd:'.$id.' ?p ?o . + FILTER(LANG(?o) = "en") . + } + UNION + { + wd:'.$id.' ?p ?o . + ?o rdfs:label ?label . + SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". } + } + } + '; + my %data = Koha::Explore::Sparql::GetPredicatesAndObjectsForSubject ( + undef,$endpoint,$sparql + ); + _llog(\%data,"author data"); + return \%data; +} + +=head2 GetDataForBiblio + Execute sparql query on Sparql endpoint. +=cut + +sub GetDataForBiblio { + my ($ark) = @_; + + my $ark_part = _get_clean_part($ark); + + my $sparql = ' + SELECT ?wdwork + WHERE { ?wdwork wdt:P268 ?idbnf FILTER CONTAINS(?idbnf, "'.$ark_part.'") . } + '; + my $iterator = Koha::Explore::Sparql::Execute ($sparql,$endpoint); + return $sparql if (! defined $iterator) ; + my $firstrow = $iterator->next; + my $wdid = $firstrow->{wdwork}[1]; + $wdid =~ /.*\/(.*)/; + my $id = $1; + + $sparql = ' + SELECT DISTINCT ?p ?o + WHERE { + { + wd:'.$id.' ?p ?o . + FILTER(LANG(?o) = "en") . + } + UNION + { + wd:'.$id.' ?p ?o . + ?o rdfs:label ?label . + SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". } + } + } + '; + my %data = Koha::Explore::Sparql::GetPredicatesAndObjectsForSubject ( + "wd:".$wdid,$endpoint,$sparql + ); + + _llog(\%data,"biblio data"); + return \%data; +} + + +#sub GetDataForAuthor { +# my ($wdworkid) = @_; +# my $workid = substr($wdworkid,30); +# _llog($workid, "AUTHIDWD"); +# +# my $sparql = ' +# SELECT ?wdauthor ?birthdate ?deathdate ?image ?workperiod ?name WHERE { +# wd:'.$workid.' wdt:P50 ?wdauthor. +# OPTIONAL { ?wdauthor wdt:P18 ?image. } +# OPTIONAL { ?wdauthor wdt:P2031 ?workperiod. } +# optional { ?wdauthor wdt:P570 ?deathdate} . +# OPTIONAL { ?wdauthor wdt:P1477 ?name. } +# OPTIONAL { ?wdauthor wdt:P569 ?birthdate. } +# } +# '; +# _llog ($sparql,"AUTHOR"); +#return $sparql; +# # Autres properties exploitables: +# # pseudonym P742 +# # occupation P106 +# # genre P136 +# # influenced by P737 +# # work period (start) P2031 +# # notable work P800 +# +# my $iterator = _execute_sparql_query ($sparql); +# return $sparql if (!defined $iterator) ; +# my $firstrow = $iterator->next; +# ## _llog ($firstrow); +# my $dataFromWD = { +# 'wdauthor' => $firstrow->{wdauthor}[1], +# 'name' => $firstrow->{name}[0], +# 'birthdate' => $firstrow->{birthdate}[0], +# 'deathdate' => $firstrow->{deathdate}[0], +# 'image' => $firstrow->{image}[1], +# 'workperiod' => $firstrow->{workperiod}[0], +# }; +# _llog ($dataFromWD); +# return $dataFromWD; +#} +# +#=head2 GetDataForBiblio +# Execute sparql query on Sparql endpoint. +#=cut +# +#sub GetDataForBiblio { +# my ($ark) = @_; +# +# my $ark_part = substr($ark,37); +# my $offset = length($ark_part) -6; +# $ark_part = substr ($ark_part, 0, $offset) if ($ark =~ m/#about/); +# return $ark_part; +# +# my $sparql = ' +# SELECT ?wdwork ?wdworkDescription ?wdauthor ?wdauthorLabel ?countryoriginLabel ?serieLabel ?pubdate ?genreLabel +# WHERE +# { +# ?wdwork wdt:P268 ?idbnf +# FILTER CONTAINS(?idbnf, "'. $ark_part .'"). +# ?wdwork wdt:P50 ?wdauthor . +# OPTIONAL { ?wdwork wdt:P495 ?countryorigin .} +# OPTIONAL { ?wdwork wdt:P577 ?pubdate .} +# OPTIONAL { ?wdwork wdt:P179 ?serie .} +# OPTIONAL { ?wdwork wdt:P136 ?genre .} +# SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],fr". } +# } +# +# '; +#return $sparql; +# +## Non gérés: +## si plusieurs résultats +## si plusieurs valeurs (ex: genre) +## la langue du service de label +# +# my $iterator = _execute_sparql_query ($sparql); +# return $sparql if (! defined $iterator) ; +# my $firstrow = $iterator->next; +# ## _llog ($firstrow); +# my $dataFromWD = { +# 'wdwork' => $firstrow->{wdwork}[1], +# 'wdworkDescription' => $firstrow->{wdworkDescription}[0], +# 'wdauthor' => $firstrow->{wdauthor}[1], +# 'wdauthorLabel' => $firstrow->{wdauthorLabel}[0], +# 'countryoriginLabel' => $firstrow->{countryoriginLabel}[0], +# 'serieLabel' => $firstrow->{serieLabel}[0], +# 'pubdate' => $firstrow->{pubDate}[0], +# 'genreLabel' => $firstrow->{genreLabel}[0], +# }; +# _llog ($dataFromWD); +# return $dataFromWD; +#} + +sub _llog { + my ($var,$a,$b) = @_; + use Data::Dumper; + $a = "" if !defined $a; + $b = "" if !defined $b; + warn Dumper($a . '##############################################'); + warn Dumper($var); + warn Dumper($b . '##############################################'); +} + +sub _get_clean_part { + my ($ark) = @_; + + $ark =~ /.*\/(.*)/; + my $ark_part = $1; + $ark_part = substr ($ark_part, 2, length($ark_part)); + + my $offset = length($ark_part) -6; + $ark_part = substr ($ark_part, 0, $offset) if ($ark =~ m/#about/); + + return $ark_part; +} + +sub _get_wd_id { + my ($wdid) = @_; + $wdid =~ /.*\/(.*)/; + return $1; +} + +=head1 AUTHOR +Claire Hernandez, Eclaire.hernandez@biblibre.comE +=cut + +1; + +__END__ --- a/Koha/Template/Plugin/Explore.pm +++ a/Koha/Template/Plugin/Explore.pm @@ -0,0 +1,54 @@ +package Koha::Template::Plugin::Explore; + + + +use Modern::Perl; + +use base qw( Template::Plugin ); + +use C4::Context; +use C4::Biblio; +use Koha; +use Koha::Explore::Wikidata; +use Koha::Explore::Bnf; + +=pod + +Explore catalog data with linked data. + +To use, first, include the line '[% USE Koha %]' at the top +of the template to enable the plugin. + +For example: [% IF Koha.Preference( 'MyPreference ) == 'SettingA' %] +removes the necessity of setting a template variable in Perl code for +each and every system preference, even if no evaluation of the setting +is necessary. + +=cut + + +sub GetAllAuthorData { + my ($self,$wddocid) = @_; + my $source = C4::Context->preference("Explore"); + return Koha::Explore::Wikidata::GetDataForAuthor($wddocid) if $source eq "Wikidata"; + return Koha::Explore::Bnf::GetDataForAuthor($wddocid) if $source eq "Bnf"; +} + +sub GetAllBiblioData { + my ($self,$ark) = @_; + my $source = C4::Context->preference("Explore"); + warn "source" . $source; + warn "ark" . $ark; + return Koha::Explore::Wikidata::GetDataForBiblio($ark) if $source eq "Wikidata"; + return Koha::Explore::Bnf::GetDataForBiblio($ark) if $source eq "Bnf"; +} + +#warn &GetAllBiblioData(undef, "http://data.bnf.fr/ark:/12148/cb347444570"); +#warn &GetAllBiblioData(undef, "http://data.bnf.fr/ark:/12148/cb13516296h"); +#&GetAllAuthorData(undef,"http://data.bnf.fr/ark:/12148/cb137554124#about"); + +#&GetAllAuthorData(undef,"https://www.wikidata.org/wiki/Q180736"); +#&GetAllAuthorData(undef,"https://www.wikidata.org/wiki/Q535"); +#&GetAllBiblioData(undef, "http://catalogue.bnf.fr/ark:/12148/cb13516296h#about"); + +1; --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/explore/bnf.inc +++ a/koha-tmpl/opac-tmpl/bootstrap/en/includes/explore/bnf.inc @@ -0,0 +1,96 @@ + + +[% + biblioData = Explore.GetAllBiblioData(biblio.abstract) +%] +
+
    +
  • Ark used : [% biblio.abstract %]
  • +
  • Query on ark for the document:
  • + [% FOREACH p IN biblioData -%] + [% FOREACH o IN p.value -%] + + [% SWITCH p.key %] + [% CASE 'http://purl.org/dc/terms/title' %] +
  • Title at la bnf: [% o %]
  • + [% CASE 'http://data.bnf.fr/ontology/bnf-onto/isbn' %] +
  • ISBN: [% o %]
  • + [% CASE 'http://data.bnf.fr/ontology/bnf-onto/ouvrageJeunesse' %] +
  • Type: Youth
  • + [% CASE 'http://data.bnf.fr/ontology/bnf-onto/subject' %] +
  • Type: [% o %]
  • + [% CASE 'http://rdvocab.info/Elements/dateOfWork' %] +
  • Work published in: [% o %]
  • + [% CASE 'http://purl.org/dc/terms/description' %] +
  • Description: [% o %]
  • + [% CASE 'http://purl.org/dc/terms/publisher' %] +
  • Publisher: [% o %]
  • + [% CASE 'http://rdvocab.info/RDARelationshipsWEMI/workManifested' %] +
  • Work: [% o %]
  • + [% + authorData = Explore.GetAllAuthorData(o) + idused = o + %] + [% CASE ['http://www.w3.org/2000/01/rdf-schema#seeAlso','http://www.w3.org/2002/07/owl#sameAs'] %] +
  • See also:[% o %]
  • + [% END %] + [% END -%] + [% END -%] +
+
+
+
    +
  • Id used : [% idused %]
  • +
  • (BNF) Query on author
  • + [% IF authorData == ''%] + Nothing to display + [% ELSE %] + [% FOREACH p IN authorData -%] + [% FOREACH o IN p.value -%] + [% SWITCH p.key %] + [% CASE 'http://xmlns.com/foaf/0.1/depiction' %] + [% IF depiction != "displayed" %] +
  • + [% END %] + [% depiction = "displayed" %] + [% CASE 'http://xmlns.com/foaf/0.1/name' %] +
  • Name: [% o %]
  • + [% CASE 'http://rdvocab.info/ElementsGr2/biographicalInformation' %] +
  • Bio : [% o %]
  • + [% CASE 'http://xmlns.com/foaf/0.1/page' %] +
  • Read on bnf.fr
  • + [% END %] + [% END -%] + [% END -%] + [% END %] +
      +
+ + + --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/explore/wikidata.inc +++ a/koha-tmpl/opac-tmpl/bootstrap/en/includes/explore/wikidata.inc @@ -0,0 +1,122 @@ + + +[% + biblioData = Explore.GetAllBiblioData(biblio.abstract) +%] +
+
    +
  • Ark used : [% biblio.abstract %]
  • +
  • (WD) Query on ark for the document:
  • + [% lo = "no" %] + [% FOREACH p IN biblioData -%] + [% SWITCH p.key %] + [% CASE 'http://www.w3.org/2000/01/rdf-schema#label' %] +
  • Title : + [% lo = "loop" %] + [% CASE 'http://schema.org/description' %] +
  • Description: + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P50' %] +
  • Author: + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P136' %] +
  • Genre : + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P110' %] +
  • Illustrated by: + [% lo = "loop" %] + [% END %] + [% IF lo == "loop" %] + [% FOREACH o IN p.value -%] + [% IF o.match("http") %] + [% o.match(".*\/(.*)").0 %] + [% ELSE %] + [% o %] + [% END -%] + [% lo = "no" %] + [% END -%] + [% END -%] + [% IF p.key == "http://www.wikidata.org/prop/direct/P50" %] + [% + authorData = Explore.GetAllAuthorData(o) + wdused = o + %] + [% END -%] +
  • + [% END -%] +
+
+
+
    +
  • WD id used : [% wdused %]
  • +
  • (WD) Query on author
  • + [% IF authorData == ''%] + Nothing to display + [% ELSE %] + [% lo = "no" %] + [% FOREACH p IN authorData -%] + [% SWITCH p.key %] + [% CASE '' %] +
  • + [% lo = "loop" %] + [% CASE 'http://www.w3.org/2000/01/rdf-schema#label' %] +
  • Name: + [% lo = "loop" %] + [% CASE 'http://schema.org/description' %] +
  • Bio : + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P106' %] +
  • Occupation : + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P166' %] +
  • Award received : + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P800' %] +
  • Notable work : + [% lo = "loop" %] + [% CASE 'http://www.wikidata.org/prop/direct/P737' %] +
  • Influenced by : + [% lo = "loop" %] + [% END %] + [% IF lo == "loop" %] + [% FOREACH o IN p.value -%] + [% IF o.match("http") %] + [% o.match(".*\/(.*)").0 %] + [% ELSE %] + [% o %] + [% END -%] + [% END -%] + [% lo = "no" %] + [% END -%] +
  • + [% END -%] + [% END %] +
      +
+ + + --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-detail.tt @@ -5,6 +5,7 @@ [% USE Branches %] [% USE ColumnsSettings %] [% USE AuthorisedValues %] +[% USE Explore %] [% SET TagsShowEnabled = ( ( Koha.Preference( 'TagsEnabled' ) == 1 ) && TagsShowOnDetail ) %] [% SET TagsInputEnabled = ( ( Koha.Preference( 'opacuserlogin' ) == 1 ) && ( Koha.Preference( 'TagsEnabled' ) == 1 ) && TagsInputOnDetail ) %] [% IF Koha.Preference('AmazonAssocTag') %] @@ -587,6 +588,7 @@ [% IF ( SyndeticsAuthorNotes && SYNDETICS_ANOTES ) %]
  • About the author
  • [% END %] + [% END # / IF SyndeticsEnabled %] [% IF ( LibraryThingForLibrariesID && LibraryThingForLibrariesTabbedView ) %] @@ -716,6 +718,7 @@ [% END # / SeparateHoldings %] + [% IF ( MARCNOTES || notes || ( SyndeticsEnabled && SyndeticsSummary && SYNDETICS_SUMMARY )) %]
    @@ -1157,8 +1160,15 @@
    + + [% END %]
    + [% IF Koha.Preference( 'Explore' ) == 'Wikidata' %] + [% INCLUDE 'explore/wikidata.inc' %] + [% ELSIF Koha.Preference( 'Explore' ) == 'Bnf' %] + [% INCLUDE 'explore/bnf.inc' %] + [% END %]
    --- a/opac/opac-detail.pl +++ a/opac/opac-detail.pl @@ -1197,4 +1197,12 @@ $template->param( 'OpacLocationBranchToDisplay' => C4::Context->preference('OpacLocationBranchToDisplay'), ); + +# Explore +if( C4::Context->preference('Explore') ) +{ +$template->param(Explore => C4::Context->preference('Explore') ); +} + + output_html_with_http_headers $query, $cookie, $template->output; --- a/t/Koha_Template_Plugin_Explore.t +++ a/t/Koha_Template_Plugin_Explore.t @@ -0,0 +1,37 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::More tests => 2; +use Test::MockModule; + +use String::Random; + +# Test the plugin is usable +use_ok( 'Koha::Template::Plugin::Explore' ); +ok( my $cache = Koha::Template::Plugin::Explore->new() ); + +subtest "Koha::Template::Plugin::Explore::GetWikidataForBiblio tests" => sub { + + plan tests => 1; + my $expected = "test"; + my $biblionumber = "273049"; + my $res = Koha::Template::Plugin::Explore::GetWikidataForBiblio($biblionumber); + is( $res, $expected , "Correct development version"); +}; + --