diff options
author | Jonas Smedegaard <dr@jones.dk> | 2013-05-20 23:37:32 +0200 |
---|---|---|
committer | Jonas Smedegaard <dr@jones.dk> | 2013-05-20 23:37:32 +0200 |
commit | c2f6227e773263be2aeaf65057c01aac9d1b101b (patch) | |
tree | b0f72764b5b29c5760a41e4a05bf4040acc0ef1e /bin/rdf2events |
Initial commit.
Diffstat (limited to 'bin/rdf2events')
-rwxr-xr-x | bin/rdf2events | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/bin/rdf2events b/bin/rdf2events new file mode 100755 index 0000000..865afe9 --- /dev/null +++ b/bin/rdf2events @@ -0,0 +1,213 @@ +#!/usr/bin/perl +# +# Copyright © 2013 Jonas Smedegaard <dr@jones.dk> +# Description: render events webpage from RDF data +# +# This program 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, or (at your option) +# any later version. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +use 5.010; +use strictures 1; +use utf8::all; +#use DDP alias => 'Dumper'; + +use DateTimeX::Easy; +use DateTime::Format::W3CDTF; +use Carp; +use Try::Tiny; +use URI; +use RDF::Query; +use RDF::TrineX::Functions qw[curie model parse]; +use HTML::HTML5::Builder qw[:standard]; +use HTML::HTML5::Writer; + +# TODO: add options --verbose and --debug + +# TODO: add override options --from, --through and --theme +#my $globalfrom = DateTimeX::Easy->new('now'); +# TODO: teach DTX::Easy / DT::F::Natural DT::F::Flexible words "current" and "present" (as alternatives to this) +my $globalfrom = DateTimeX::Easy->new('first day of this month'); +# TODO: teach DTX::Easy / DT::F::Natural DT::F::Flexible these: +# last day of month three months from now +# last day of month in three months +# last day of (the) month three months ahead +# ... in the future +# ... into the future +# ... from now +# ... later <- this last one for changing object, not create new +#my $globalthrough = DateTimeX::Easy->new('last day of month in 3 months'); +my $globalthrough = DateTimeX::Easy->new($globalfrom); +$globalthrough->add( months => 4 )->subtract( days => 1 ); + +# TODO: add Getopt option --preset as shortcut to --category, --from and --through + +my $parser = RDF::Trine::Parser::Turtle->new; +my $model = model(); + +# TODO: throw sensible error if no (non-opt) args provided +while (my $data = shift @ARGV) { + try { + parse($data, using => $parser, into => $model); + } catch { + say STDERR "Failed to parse file \"$data\": $_"; + die; + } +} + +# TODO: handle multi-site events-in-events (e.g. Orødagen) + +# compose query for hourspec data at least partly within a "window" of time +# (yes, comparing global dates against opposite spec dates is deliberate) +my $w3c = DateTime::Format::W3CDTF->new; +my $globalfrom_w3c = $w3c->format_datetime($globalfrom); +my $globalthrough_w3c = $w3c->format_datetime($globalthrough); +# TODO: restructure SPARQL query to only need injecting each date once +my $query = qurie( sprintf(<<'SPARQL', $globalfrom_w3c, $globalthrough_w3c, $globalfrom_w3c, $globalthrough_w3c )); +SELECT * +WHERE { + ?event + a schema:SocialEvent ; + schema:name ?eventname ; + schema:location ?place ; + schema:startDate ?start . + ?place + a schema:Place ; + gr:name|schema:name ?placename . + OPTIONAL { + ?event schema:endDate ?end . + } + OPTIONAL { + ?event foaf:Homepage ?eventpage . + } + OPTIONAL { + { ?organizer a schema:Organization } + UNION + { ?organizer a gr:BusinessEntity } + ?organizer + gr:name|schema:name ?organizername ; + schema:event ?event . + OPTIONAL { + ?organizer foaf:Homepage ?organizerpage . + } + } + OPTIONAL { + { ?host a schema:Organization } + UNION + { ?host a gr:BusinessEntity } + ?host + gr:name|schema:name ?hostname ; + schema:location ?place . + OPTIONAL { + ?host foaf:Homepage ?hostpage . + } + } . + FILTER ( + ( ! bound(?end) && ?start >= "%s"^^xsd:dateTime && ?start <= "%s"^^xsd:dateTime ) + || + ( ?start < ?end && ?end > "%s"^^xsd:dateTime && ?start <= "%s"^^xsd:dateTime ) + ) +} +ORDER BY ?start ?eventname ?placename +SPARQL +# FILTER ( ?start > "%s"^^xsd:dateTime && ?start <= "%s"^^xsd:dateTime ) + +my $title = 'Begivenheder'; +my $intro = sprintf( + 'Begivenheder på Orø i perioden %s.', + daterangedescribe($globalfrom, $globalthrough), +); + +# TODO: make simplified html (preferred for Scribus) optional +#push my @content, "\n", h1($title), p($intro); +push my @content, "\n", h1($title), $intro; + +my $iterator = $query->execute($model); +while ( my $row = $iterator->next ) { + + # TODO: iterate over potentially plural entries + # TODO: include also foaf:page, and support multiple pages + # TODO: handle dcterms:audience (including lack thereof) + push @content, "\n", h2($row->{eventname}->literal_value); + push @content, thingdescribe( 'Begivenhed', daterangedescribe( $row->{start}->datetime, $row->{end} ? $row->{end}->datetime : '' ), node2uri($row->{eventpage}) ); + push @content, objectdescribe( 'Sted', $row->{placename}, $row->{placepage} ); + push @content, objectdescribe( 'Vært', $row->{hostname}, $row->{hostpage} ); + push @content, objectdescribe( 'Arrangør', $row->{organizername}, $row->{organizerpage} ); +} + +my $writer = HTML::HTML5::Writer->new; +say $writer->document( html( + -lang => 'da', + head( + title($title), + Meta(-charset => 'utf-8'), + ), + body( + @content + ), +)); + +# create query object from curied SPARQL string +sub qurie { + my $sparql = shift; + $sparql =~ s/\b([a-z]+):([a-zA-Z]+)\b/curie("$1_$2")/eg; + my $query = RDF::Query->new( $sparql ); + if (!$query) { + say STDERR $sparql; + say STDERR RDF::Query->error; + croak "failed to prepare SPARQL query"; + } + return $query; +} + +sub daterangedescribe { + my ($from, $through) = @_; + + return $from->format_cldr('d/M') + unless ($through); + return sprintf( '%s - %s', + $from->format_cldr('d/M'), + $through->format_cldr('d/M'), + ); +} + +sub thingdescribe { + my ($title, $thing, $uri) = @_; + my $string; + + return undef unless ($thing); + + $string = ($uri) + ? a('-href', $uri, $thing) + : $thing; + + return "\n", br, div( "$title: ", $string ); +} + +sub objectdescribe { + my ($title, $thing, $uri) = @_; + + return undef unless ($thing); + return thingdescribe( $title, $thing->literal_value, undef ) + unless ($uri); + return thingdescribe( $title, $thing->literal_value, node2uri($uri) ); +} + +# TODO: report as bug against RDF::Trine RDF::Trine::Node->uri_value not utf8 +# i.e. choking on punycode if fed to HTML::HTML5::Builder +sub node2uri { + my $node = shift; + + return ($node) ? URI->new($node->uri_value)->as_string : undef; +} + +1; |