App-skos2jskos
view release on metacpan or search on metacpan
script/skos2jskos view on Meta::CPAN
my %opt;
GetOptions(
\%opt, 'help|h|?', 'sparql|q=s', 'scheme|s=s',
'directory|d=s', 'name|n=s', 'language|l=s', 'quiet|q',
'verbose|v', 'debug', 'version',
) or exit 1;
say "skos2jskos $VERSION" and exit if $opt{version};
if ( $opt{help} or ( !$opt{sparql} && !@ARGV ) ) {
pod2usage( -exitval => 1, -indent => 2 );
}
$opt{directory} //= '.';
$opt{directory} =~ s{/$}{};
die "output directory not found: " . $opt{directory} . "\n"
unless -d $opt{directory};
$opt{language} //= 'en';
$opt{verbose} = 2 if $opt{debug};
## Logging methods
use Term::ANSIColor;
my $colored = -t STDOUT; ## no critic
sub error($) { ## no critic
say STDERR ( $colored ? colored( $_[0], 'red' ) : $_[0] );
}
sub fatal($) { ## no critic
error $_[0];
exit 1;
}
sub warning($) { ## no critic
say STDERR ( $colored ? colored( $_[0], 'yellow' ) : $_[0] );
}
sub info($) { ## no critic
return if $opt{quiet};
say( $colored ? colored( $_[0], 'green' ) : $_[0] );
}
sub debug($) { ## no critic
return unless $opt{verbose};
say( $colored ? colored( $_[0], 'white' ) : $_[0] );
}
sub trace($) { ## no critic
return unless $opt{verbose} > 1;
say $_[0];
}
## check where to get RDF data from
use RDF::Trine;
use RDF::Query;
use RDF::Query::Client;
my $source = $opt{sparql} || RDF::Trine::Model->new;
if ( $opt{sparql} ) {
info "Getting RDF from SPARQL endpoint " . $opt{sparql};
}
elsif ( $ARGV[0] =~ qr{^https?://} ) {
info "Reading RDF from $ARGV[0]";
RDF::Trine->default_useragent->ssl_opts( verify_hostname => 0 );
RDF::Trine::Parser->parse_url_into_model( $ARGV[0], $source );
debug $source->size . " triples";
}
else {
info "Reading RDF files";
my $size = 0;
foreach my $file (@ARGV) {
my $parser = RDF::Trine::Parser->guess_parser_by_filename($file);
$parser->parse_file_into_model( "file://$file", $file, $source );
debug $source->size - $size . " triples from $file";
$size = $source->size;
}
}
### SPARQL query method
sub sparql {
my ($query) = @_;
$query = <<'SPARQL'. $query . "\n}";
PREFIX dct: <http://purl.org/dc/terms/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX vann: <http://purl.org/vocab/vann/>
SELECT * WHERE {
SPARQL
trace $query;
my $q =
$opt{sparql}
? RDF::Query::Client->new($query)
: RDF::Query->new($query);
$q->execute($source) // RDF::Trine::Iterator->new( [], 'bindings', [] );
}
sub language {
$_[0]->literal_value_language // do {
warning "missing language tag of " . $_[0];
$opt{language};
};
}
## Conversion methods
sub prefLabel {
my ( $entity, $label ) = @_;
return unless isLiteral( $label, 'prefLabel' );
my $language = language($label);
my $value = $label->literal_value;
if ( ( $entity->{prefLabel}->{$language} // $value ) ne $value ) {
warning "multiple prefLabel\@$language for " . $entity->{uri};
}
$entity->{prefLabel}->{$language} = $value;
}
sub equal {
my ( $a, $b ) = @_;
script/skos2jskos view on Meta::CPAN
qw(scopeNote definition example historyNote editorialNote changeNote note);
my $concepts = {};
# TODO: just all concepts instead of inScheme/topConceptOf
# TODO: broader, altLabel etc.
# TODO: created, modified
my $query = <<SPARQL;
{ { ?c skos:inScheme <$opt{scheme}> } UNION { ?c skos:topConceptOf <$opt{scheme}> } } .
OPTIONAL { ?c skos:prefLabel ?pLabel } .
OPTIONAL { ?c skos:notation ?notation } .
OPTIONAL { ?c skos:narrower ?narrower } .
OPTIONAL { ?c skos:broader ?broader } .
OPTIONAL { ?c skos:topConceptOf ?top } .
SPARQL
$query = join "\n", $query, map { "OPTIONAL { ?c skos:$_ ?$_ } ." } @noteTypes;
sparql($query)->each(
sub {
my $row = shift;
my $uri = $row->{c}->uri_value;
unless ( defined $concepts->{$uri} ) {
debug "$uri";
$concepts->{$uri} = {
uri => $uri,
type => ['http://www.w3.org/2004/02/skos/core#Concept'],
inScheme => [ { uri => $opt{scheme} } ],
};
$concepts->{$uri}{topConceptOf} = $concepts->{$uri}{inScheme}
if ($row->{top} && $row->{top}->uri_value eq $scheme->{uri}) ||
grep {$_->{uri} eq $uri} @{$scheme->{topConcepts} || []};
}
my $concept = $concepts->{$uri};
notation( $concept, $row->{notation} );
prefLabel( $concept, $row->{pLabel} );
note( $concept, $row->{$_}, $_ ) for @noteTypes;
uriSet( $concept, 'narrower', $row->{narrower} );
uriSet( $concept, 'broader', $row->{broader} );
}
);
my $count = values %$concepts or warning "No concepts found";
info "Exporting $count JSKOS concepts";
export( 'concepts.ndjson', [ map { $concepts->{$_} } sort keys %$concepts ] );
__END__
=head1 NAME
skos2jskos - convert SKOS/RDF to JSKOS
=head1 SYNOPSIS
skos2jskos OPTIONS [ FILES | URL ]
=head1 USAGE
This script can be used to convert SKOS data from local RDF files, URL, or
SPARQL-endpoint to L<JSKOS format|https://gbv.github.io/jskos/> (SKOS in
JSON-LD). The script is aligned with JSKOS 0.4.4 but it does not cover all
possible fields yet.
On success the following files are created in normalized JSON:
=over
=item scheme.json
JSKOS description of the concept scheme
=item concepts.ndjson
JKSOS description of all concepts
=back
Location of the files can be controlled with option C<--directory> and option
C<--name> can be used to prepend a prefix to each file.
Conversion is not optimized for speed so be patient!
=head2 SKOS requirements
The concept scheme MUST have a title (C<dct:title>).
All concepts MUST be linked from the concept scheme via C<skos:inScheme> or
C<skos:topConceptOf>.
=head1 OPTIONS
=over
=item --directory | -d
Output directory to write JSKOS files to. Current directory by default.
=item --name | -n
Filename prefix to append to exported files. E.g. C<--name foo> will create
C<foo-scheme.json> and C<foo-concepts.ndjson>.
=item --language | -l
Default language to use for literal values without language (C<en> by default).
=item --quiet | -q
Don't show status messages
=item --verbose | -v
Show detailed processing messages
=item --debug
Show more detailed processing information
=item --scheme | -s
Concept scheme URI
=item --sparql | -q
SPARQL endpoint
=item --help | -h | -?
Show usage description
=item --version
Show version of this script
=back
=head1 SEE ALSO
L<App::skos2jskos>
=cut
( run in 3.487 seconds using v1.01-cache-2.11-cpan-63c85eba8c4 )