File Coverage

blib/lib/DAIA/Availability.pm
Criterion Covered Total %
statement 45 125 36.0
branch 6 86 6.9
condition 1 46 2.1
subroutine 10 15 66.6
pod 5 5 100.0
total 67 277 24.1


line stmt bran cond sub pod time code
1 1     1   23021 use strict;
  1         2  
  1         39  
2 1     1   5 use warnings;
  1         2  
  1         53  
3             package DAIA::Availability;
4             #ABSTRACT: Abstract base class of availability information
5             our $VERSION = '0.43'; #VERSION
6              
7 1     1   6 use base 'DAIA::Object';
  1         1  
  1         603  
8              
9 1     1   6 use Carp::Clan;
  1         2  
  1         8  
10 1     1   134 use Data::Validate::URI qw(is_uri);
  1         1  
  1         52  
11 1     1   1007 use DateTime::Duration;
  1         172822  
  1         29  
12 1     1   1044 use DateTime::Format::Duration;
  1         5485  
  1         44  
13              
14 1     1   6 use DateTime;
  1         1  
  1         17  
15 1     1   5 use base 'Exporter';
  1         1  
  1         1721  
16             our @EXPORT_OK = qw(parse_duration normalize_duration date_or_datetime);
17              
18              
19             our %PROPERTIES = (
20             service => {
21             # default => sub { croak 'DAIA::Availability->service is required' },
22             default => sub { undef }, # TODO: configure whether mandatory
23             filter => sub {
24             my $s = $_[0];
25             return $s if $DAIA::Availability::SERVICES{$s};
26             return $DAIA::Availability::SECIVRES{$s} if $DAIA::Availability::SECIVRES{$s};
27             return $s if is_uri($s); return;
28             }
29             },
30             href => $DAIA::Object::COMMON_PROPERTIES{href},
31             message => $DAIA::Object::COMMON_PROPERTIES{message},
32             limitation => {
33             type => 'DAIA::Limitation',
34             repeatable => 1,
35             }
36             );
37              
38             # known services
39             our %SERVICES = (
40             'presentation' => 'http://purl.org/ontology/daia/Service/Presentation',
41             'loan' => 'http://purl.org/ontology/daia/Service/Loan',
42             'interloan' => 'http://purl.org/ontology/daia/Service/Interloan',
43             'openaccess' => 'http://purl.org/ontology/daia/Service/Openaccess',
44             );
45              
46             our %SECIVRES = (
47             map { $SERVICES{$_} => $_ } keys %SERVICES
48             );
49              
50              
51             sub _buildargs {
52 0     0   0 my $self = shift;
53 0         0 my %args = ();
54              
55 0 0       0 if ( not (@_ % 2) ) { # even number
    0          
56 0         0 %args = @_;
57 0 0       0 if ( not defined $args{status} ) { # $service => $status
58 0         0 foreach ( keys %DAIA::Availability::SERVICES ) {
59 0 0       0 if ( defined $args{$_} ) {
60 0         0 $args{status} = $args{$_};
61 0         0 $args{service} = $_;
62 0         0 delete $args{$_};
63             }
64             }
65             }
66             } elsif ( @_ ) { # non empty, uneven number
67 0 0 0     0 if ( @_ == 1 and UNIVERSAL::isa( $_[0], 'DAIA::Availability' ) ) {
    0 0        
68 0         0 %args = %{ $_[0]->struct };
  0         0  
69 0         0 $self = $_[0];
70             } elsif ( $DAIA::Availability::SERVICES{$_[0]} or is_uri($_[0]) ) {
71 0         0 %args = ( service => @_ );
72             } else {
73 0         0 croak( "could not parse parameters to " . ref($self) );
74             }
75             }
76            
77 0 0       0 if ( not defined $args{status} ) {
78 0 0       0 if ( ref($self) eq 'DAIA::Available' ) {
    0          
79 0         0 $args{status} = 1;
80             } elsif ( ref($self) eq 'DAIA::Unavailable' ) {
81 0         0 $args{status} = 0;
82             }
83             }
84              
85 0         0 return %args;
86             }
87              
88              
89             sub status {
90 0     0 1 0 my $self = shift;
91 0         0 my $class = ref($self);
92 0         0 my $status;
93              
94 0 0       0 if ( @_ > 0 ) {
95 0         0 $status = shift;
96 0 0       0 if ( $status ) {
97 0 0       0 if ( $class eq 'DAIA::Unavailable' ) {
98 0         0 $self->expected( undef );
99 0         0 $self->queue( undef );
100             }
101 0         0 bless $self, 'DAIA::Available';
102              
103             } else {
104 0 0       0 if ( $class eq 'DAIA::Available' ) {
105 0         0 $self->delay( undef );
106             }
107 0         0 bless $self, 'DAIA::Unavailable';
108             }
109             } else {
110 0         0 $status = $class eq 'DAIA::Available';
111             }
112              
113 0         0 return $status;
114             }
115              
116             sub rdfhash {
117 0     0 1 0 my $self = shift;
118 0         0 my $me = { };
119              
120 0   0     0 my $servicetype = $DAIA::Availability::SERVICES{ $self->{service} }
121             || $self->{service};
122              
123 0 0       0 $me->{'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'} = [{
124             value => $servicetype, type => "uri"
125             }] if $servicetype;
126              
127 0 0       0 $me->{'http://xmlns.com/foaf/0.1/page'} = [{
128             value => $self->{href}, type => "uri"
129             }] if $self->{href};
130              
131 0         0 $me->{'http://purl.org/dc/terms/description'} = [
132 0 0       0 map { $_->rdfhash } @{$self->{message}}
  0         0  
133             ] if $self->{message};
134              
135 0 0       0 $me->{'http://purl.org/ontology/daia/expected'} = [{
136             value => $self->{expected}, type => 'literal',
137             # xsd:date or xsd:dateTime to 'unknown'
138             }] if $self->{expected};
139              
140 0 0       0 $me->{'http://purl.org/ontology/daia/delay'} = [{
141             value => $self->{delay}, type => 'literal',
142             # TODO: how to treat delay = "unknown"?
143             # datatype => 'http://www.w3.org/2001/XMLSchema#duration',
144             }] if $self->{delay};
145              
146 0 0       0 $me->{'http://purl.org/ontology/daia/delay'} = [{
147             value => $self->{queue}, type => 'literal',
148             datatype => 'http://www.w3.org/2001/XMLSchema#integer',
149             }] if defined $self->{queue};
150              
151 0 0       0 if ($self->{limitation}) {
152             # TODO
153             # some limitations may be literal, some may be uri/blank
154             # 'limitedBy'
155             }
156            
157 0         0 my $rdf = { };
158              
159 0         0 $rdf->{ $self->rdfuri } = $me;
160              
161 0         0 return $rdf;
162             }
163              
164              
165             sub parse_duration {
166 0 0   0 1 0 return $_[0] if UNIVERSAL::isa( $_[0], 'DateTime::Duration' );
167 0         0 my $duration = "$_[0]";
168              
169 0         0 my ($neg, $year, $mounth, $day, $hour, $min, $sec, $fsec);
170 0 0       0 if ($duration =~ /^(-)?
171             P
172             ((\d+)Y)?
173             ((\d+)M)?
174             ((\d+)D)?
175             (
176             T
177             ((\d+)H)?
178             ((\d+)M)?
179             (((\d+)(\.(\d+))?)S)?
180             )?
181             $/x) {
182 0         0 ($neg, $year, $mounth, $day, $hour, $min, $sec, $fsec) =
183             ($1, $3, $5, $7, $10, $12, $15, $17);
184 0 0       0 return unless (grep {defined} ($year, $mounth, $day, $hour, $min, $sec));
  0         0  
185             } else {
186 0         0 return;
187             }
188 0 0 0     0 $duration = DateTime::Duration->new(
      0        
      0        
      0        
      0        
      0        
189             years => $year || 0,
190             months => $mounth || 0,
191             days => $day || 0,
192             hours => $hour || 0,
193             minutes => $min || 0,
194             seconds => $sec || 0,
195             nanoseconds => ($fsec ? "0.$fsec" * 1E9 : 0),
196             );
197 0 0       0 $duration = $duration->inverse if $neg;
198 0         0 return $duration;
199             }
200              
201              
202             sub normalize_duration {
203 0     0 1 0 my $duration = $_[0];
204 0 0       0 $duration = parse_duration( $duration )
205             unless UNIVERSAL::isa( $duration, 'DateTime::Duration' );
206 0 0       0 return unless defined $duration;
207              
208 0 0       0 return "P0D" if $duration->is_zero;
209              
210             # TODO: replace this
211 0         0 my $fmt = DateTime::Format::Duration->new(
212             pattern => '%PP%YY%mM%dDT%HH%MM%S.%NS',
213             normalize => 1,
214             );
215              
216 0         0 my %d = $fmt->normalize( $duration );
217 0 0 0     0 if (exists $d{seconds} or exists $d{nanoseconds}) {
218 0 0 0     0 $d{seconds} = ($d{seconds} || 0)
219             + (exists $d{nanoseconds} ? $d{nanoseconds} / 1E9 : 0);
220             }
221 0 0       0 my $str = $d{negative} ? "-P" : "P";
222 0 0 0     0 $str .= "$d{years}Y" if exists $d{years} and $d{years} > 0;
223 0 0 0     0 $str .= "$d{months}M" if exists $d{months} and $d{months} > 0;
224 0 0 0     0 $str .= "$d{days}D" if exists $d{days} and $d{days} > 0;
225 0 0       0 $str .= "T" if grep {exists $d{$_} and $d{$_} > 0} qw(hours minutes seconds);
  0 0       0  
226 0 0 0     0 $str .= "$d{hours}H" if exists $d{hours} and $d{hours} > 0;
227 0 0 0     0 $str .= "$d{minutes}M" if exists $d{minutes} and $d{minutes} > 0;
228 0 0 0     0 $str .= "$d{seconds}S" if exists $d{seconds} and $d{seconds} > 0;
229              
230 0         0 return $str;
231             }
232              
233              
234             sub date_or_datetime {
235 3     3 1 1408 my $dt = $_[0];
236 3 50       23 if ( not UNIVERSAL::isa( $dt, 'DateTime' ) ) {
237             return unless
238 3 50       24 $dt =~ /^(-?\d\d\d\d+-\d\d-\d\d)(T\d\d:\d\d:\d\d(\.\d+)?)?([+-]\d\d:\d\d|Z)?$/;
239 3         11 my ($date,$time,$tz) = ($1,$2,$4);
240 3         11 $date =~ /(-?\d\d\d\d+)-(\d\d)-(\d\d)/;
241 3         17 my %p = (year=>$1,month=>$2,day=>$3);
242 3 100       7 if ($time) {
243 1         5 $time =~ /T(\d\d):(\d\d):(\d\d)(\.\d+)?/;
244 1         5 ($p{hour},$p{minute},$p{second})=($1,$2,$3);
245             }
246 3 100       8 if ($tz) {
247 1         3 $tz =~ s/://; $tz =~ s/Z/UTC/;
  1         3  
248 1         2 $p{time_zone} = $tz;
249             }
250 3   50     5 $dt = eval { DateTime->new(%p) } || return;
251             }
252 3         950 $dt->set_time_zone('floating');
253              
254 3         187 my $date = $dt->strftime("%FT%T");
255 3         291 $dt =~ s/T00:00:00$//; # remove time part if is zero
256 3         124 return $dt;
257             }
258              
259             1;
260              
261             __END__