File Coverage

blib/lib/App/Web/Comstock.pm
Criterion Covered Total %
statement 33 115 28.7
branch 0 18 0.0
condition 0 13 0.0
subroutine 11 23 47.8
pod 0 12 0.0
total 44 181 24.3


line stmt bran cond sub pod time code
1             package App::Web::Comstock;
2              
3 1     1   12939 use 5.010000;
  1         2  
4 1     1   4 use strict;
  1         1  
  1         32  
5 1     1   3 use warnings;
  1         3  
  1         34  
6             our $VERSION = '0.000_001';
7              
8 1     1   736 use DateTime;
  1         70433  
  1         32  
9 1     1   477 use DBIx::Simple;
  1         16548  
  1         26  
10 1     1   754 use HTML::TreeBuilder;
  1         21210  
  1         10  
11 1     1   701 use HTML::Element::Library;
  1         29192  
  1         27  
12 1     1   403 use Plack::Builder;
  1         11651  
  1         66  
13 1     1   445 use Plack::Request;
  1         54643  
  1         48  
14 1     1   9 use POSIX qw/strftime/;
  1         2  
  1         10  
15              
16             sub HTML::Element::iter3 {
17 0     0 0 0 my ($self, $data, $code) = @_;
18 0         0 my $orig = $self;
19 0         0 my $prev = $orig;
20 0         0 for my $el (@$data) {
21 0         0 my $current = $orig->clone;
22 0         0 $code->($el, $current);
23 0         0 $prev->postinsert($current);
24 0         0 $prev = $current;
25             }
26 0         0 $orig->detach;
27             }
28              
29 0     0 0 0 sub HTML::Element::fid { shift->look_down(id => shift) }
30 0     0 0 0 sub HTML::Element::fclass { shift->look_down(class => qr/\b$_[0]\b/) }
31              
32             ##################################################
33              
34             my ($index);
35              
36             {
37             sub parse_html {
38 1     1 0 8 my $builder = HTML::TreeBuilder->new;
39 1         320 $builder->ignore_unknown(0);
40 1         19 $builder->parse_file("tmpl/$_[0].html");
41 1         5869 $builder
42             }
43              
44             $index = parse_html 'index';
45             }
46              
47             sub db : lvalue {
48 0     0 0   shift->{'comstock.db'}
49             }
50              
51             sub nav_li {
52 0     0 0   my ($data, $li) = @_;
53 0           $li->find('a')->replace_content($data->{title});
54 0           $li->find('a')->attr(href => '?item='.$data->{item});
55             }
56              
57             sub nav_ul {
58 0     0 0   my ($data, $ul) = @_;
59 0           $ul->find('li')->iter3($data, \&nav_li);
60             }
61              
62             sub display_app {
63 0     0 0   my ($env) = @_;
64 0           my $req = Plack::Request->new($env);
65 0           my $tree = $index->clone;
66 0           my @items = db($env)->select(items => '*')->hashes;
67 0           my %items;
68              
69 0           for my $item (@items) {
70 0   0       $items{$item->{category}} //= [];
71 0           push @{$items{$item->{category}}}, $item
  0            
72             }
73              
74 0           my @data = sort { $a->[0]{category} cmp $b->[0]{category} } values %items; #map { $items{$_} } sort keys %items;
  0            
75 0           $tree->fid('comstock_nav')->find('ul')->iter3(\@data, \&nav_ul);
76 0           my $item = $req->param('item');
77 0 0         if ($item) {
78 0           $tree->look_down(name => 'item')->attr(value => $item);
79 0           my ($begin, $end) = db($env)->select(items => [qw/begin_hour end_hour/], {item => $item})->list;
80 0           for my $name (qw/begin_hour end_hour/) {
81 0           my $select = $tree->look_down(name => $name);
82 0           $select->iter($select->find('option') => $begin .. $end)
83             }
84             } else {
85 0           $tree->fid('book_div')->detach
86             }
87 0           $tree
88             }
89              
90             sub error {
91 0     0 0   'Error: ' . $_[0]
92             }
93              
94             sub book_app {
95 0     0 0   my ($env) = @_;
96 0           my $req = Plack::Request->new($env);
97 0           my ($begin_year, $begin_month, $begin_day) = split '/', $req->param('begin');
98 0           my ($end_year, $end_month, $end_day) = split '/', $req->param('end');
99 0           my $begin_hour = $req->param('begin_hour');
100 0           my $end_hour = $req->param('end_hour');
101 0           my $begin = DateTime->new(year => $begin_year, month => $begin_month, day => $begin_day, hour => $begin_hour)->epoch;
102 0           my $end = DateTime->new(year => $end_year, month => $end_month, day => $end_day, hour => $end_hour)->epoch;
103 0           my $item = 0+$req->param('item');
104 0 0         my ($begin_range, $end_range, $min_hours, $max_hours) = db($env)->select(items => [qw/begin_hour end_hour min_hours max_hours/], {item => $item})->list or return error 'No such item';
105              
106 0 0         return error 'End time is not later than begin time' if $end <= $begin;
107 0 0 0       return error 'Begin/end hour not in allowed range' if $begin_hour < $begin_range || $begin_hour > $end_range || $end_hour < $begin_range || $end_hour > $end_range;
      0        
      0        
108 0 0         return error 'Bookings must last for at least $min_hours hours' if (($end - $begin) / 3600 < $min_hours);
109 0 0         return error 'Bookings must last for at most $max_hours hours' if (($end - $begin) / 3600 > $max_hours);
110 0 0         return error 'Item is not available for the selected period' if db($env)->query('SELECT item FROM bookings WHERE item = ? AND (end_time - begin_time + ? - ?) > GREATEST(end_time, ?) - LEAST(begin_time, ?)', $item, $end, $begin, $end, $begin)->list;
111 0           db($env)->insert(bookings => {
112             item => $item,
113             name => scalar $req->param('name'),
114             begin_time => $begin,
115             end_time => $end,
116             });
117 0           return [200, ['Content-Type' => 'text/plain'], ['Booking was successful']];
118             }
119              
120             sub view_app {
121 0     0 0   my ($env) = @_;
122 0           my $req = Plack::Request->new($env);
123 0           my $item = $req->param('item');
124 0           my $time = time;
125 0           $time -= $time % 86400;
126 0           my @bookings = db($env)->select(bookings => '*', {item => $item, begin_time => {'>', $time}}, 'begin_time')->hashes;
127 0           my $ans;
128 0           for my $booking (@bookings) {
129 0           $booking->{name} =~ y/\n//d;
130 0           $ans .= sprintf "%s -> %s %s\n", strftime ('%c', gmtime $booking->{begin_time}), strftime ('%c', gmtime $booking->{end_time}), $booking->{name};
131             }
132 0           [200, ['Content-type' => 'text/plain'], [$ans]]
133             }
134              
135             sub app {
136             builder {
137 0     0     enable 'ContentLength';
138             enable sub {
139 0           my $app = shift;
140 0   0       my $db = DBIx::Simple->connect($ENV{COMSTOCK_DSN} // 'dbi:Pg:');
141             sub {
142 0           my ($env) = @_;
143 0           db($env) = $db;
144 0           my $res = $app->($env);
145 0 0         return $res if ref $res eq 'ARRAY';
146 0 0         return [200, ['Content-type' => 'text/html; charset=utf-8'], [$res->as_HTML]]
147             if ref $res;
148 0           return [500, ['Content-type' => 'text/plain'], ["$res"]]
149             }
150 0           };
  0            
151 0           mount '/book' => \&book_app;
152 0           mount '/view' => \&view_app;
153 0           mount '/' => \&display_app;
154             }
155 0     0 0   }
156              
157             1;
158             __END__