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__ |