| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::JobLog::Log::Synopsis; | 
| 2 |  |  |  |  |  |  | $App::JobLog::Log::Synopsis::VERSION = '1.041'; | 
| 3 |  |  |  |  |  |  | # ABSTRACT: consolidates App::JobClock::Log::Event objects for display | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 2 |  |  | 2 |  | 1307 | use Exporter 'import'; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 218 |  | 
| 7 |  |  |  |  |  |  | our @EXPORT_OK = qw( | 
| 8 |  |  |  |  |  |  | collect | 
| 9 |  |  |  |  |  |  | MERGE_ALL | 
| 10 |  |  |  |  |  |  | MERGE_ADJACENT | 
| 11 |  |  |  |  |  |  | MERGE_ADJACENT_SAME_TAGS | 
| 12 |  |  |  |  |  |  | MERGE_SAME_TAGS | 
| 13 |  |  |  |  |  |  | MERGE_SAME_DAY | 
| 14 |  |  |  |  |  |  | MERGE_SAME_DAY_SAME_TAGS | 
| 15 |  |  |  |  |  |  | MERGE_NONE | 
| 16 |  |  |  |  |  |  | ); | 
| 17 |  |  |  |  |  |  | our %EXPORT_TAGS = ( | 
| 18 |  |  |  |  |  |  | merge => [ | 
| 19 |  |  |  |  |  |  | qw( | 
| 20 |  |  |  |  |  |  | MERGE_ALL | 
| 21 |  |  |  |  |  |  | MERGE_ADJACENT | 
| 22 |  |  |  |  |  |  | MERGE_ADJACENT_SAME_TAGS | 
| 23 |  |  |  |  |  |  | MERGE_SAME_TAGS | 
| 24 |  |  |  |  |  |  | MERGE_SAME_DAY | 
| 25 |  |  |  |  |  |  | MERGE_SAME_DAY_SAME_TAGS | 
| 26 |  |  |  |  |  |  | MERGE_NONE | 
| 27 |  |  |  |  |  |  | ) | 
| 28 |  |  |  |  |  |  | ] | 
| 29 |  |  |  |  |  |  | ); | 
| 30 |  |  |  |  |  |  |  | 
| 31 | 2 |  |  | 2 |  | 12 | use Modern::Perl; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 16 |  | 
| 32 | 2 |  |  | 2 |  | 313 | use autouse 'Carp'              => qw(carp); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 15 |  | 
| 33 | 2 |  |  | 2 |  | 188 | use autouse 'App::JobLog::Time' => qw(now); | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 10 |  | 
| 34 | 2 |  |  | 2 |  | 152 | use Class::Autouse qw(DateTime); | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 20 |  | 
| 35 | 2 |  |  | 2 |  | 127 | no if $] >= 5.018, warnings => "experimental::smartmatch"; | 
|  | 2 |  |  |  |  | 14 |  | 
|  | 2 |  |  |  |  | 17 |  | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 2 |  |  | 2 |  | 143 | use constant MERGE_ALL                => 1; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 128 |  | 
| 38 | 2 |  |  | 2 |  | 12 | use constant MERGE_ADJACENT           => 2; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 124 |  | 
| 39 | 2 |  |  | 2 |  | 10 | use constant MERGE_ADJACENT_SAME_TAGS => 3; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 90 |  | 
| 40 | 2 |  |  | 2 |  | 10 | use constant MERGE_SAME_TAGS          => 4; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 111 |  | 
| 41 | 2 |  |  | 2 |  | 10 | use constant MERGE_SAME_DAY           => 5; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 91 |  | 
| 42 | 2 |  |  | 2 |  | 10 | use constant MERGE_SAME_DAY_SAME_TAGS => 6; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 82 |  | 
| 43 | 2 |  |  | 2 |  | 10 | use constant MERGE_NONE               => 0; | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 4702 |  | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | # takes in a bunch of App::JobClock::Log::Event objects | 
| 47 |  |  |  |  |  |  | # returns a bunch of App::JobClock::Log::Synopsis objects | 
| 48 |  |  |  |  |  |  | sub collect { | 
| 49 | 8000 |  |  | 8000 | 1 | 17865 | my ( $day, $merge_level ) = @_; | 
| 50 | 8000 |  |  |  |  | 9753 | my ( @synopses, $previous, @current_day ); | 
| 51 | 8000 |  |  |  |  | 9127 | for my $e ( @{ $day->events }, @{ $day->vacation } ) { | 
|  | 8000 |  |  |  |  | 21984 |  | 
|  | 8000 |  |  |  |  | 20101 |  | 
| 52 | 200 |  |  |  |  | 301 | my $do_merge = 0; | 
| 53 | 200 |  |  |  |  | 246 | my $mergand  = $previous; | 
| 54 | 200 | 100 |  |  |  | 508 | if ($previous) { | 
| 55 | 124 |  |  |  |  | 204 | for ($merge_level) { | 
| 56 | 124 |  |  |  |  | 169 | when (MERGE_ALL)      { $do_merge = 1 } | 
|  | 0 |  |  |  |  | 0 |  | 
| 57 | 124 |  |  |  |  | 142 | when (MERGE_ADJACENT) { $do_merge = $previous->adjacent($e) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 58 | 124 |  |  |  |  | 131 | when (MERGE_SAME_TAGS) { | 
| 59 | 0 |  |  |  |  | 0 | for my $o (@synopses) { | 
| 60 | 0 | 0 |  |  |  | 0 | if ( $o->same_tags($e) ) { | 
| 61 | 0 |  |  |  |  | 0 | $mergand  = $o; | 
| 62 | 0 |  |  |  |  | 0 | $do_merge = 1; | 
| 63 | 0 |  |  |  |  | 0 | last; | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  | } | 
| 66 |  |  |  |  |  |  | } | 
| 67 | 124 |  |  |  |  | 132 | when (MERGE_SAME_DAY) { $do_merge = 1 } | 
|  | 0 |  |  |  |  | 0 |  | 
| 68 | 124 |  |  |  |  | 143 | when (MERGE_SAME_DAY_SAME_TAGS) { | 
| 69 | 0 |  |  |  |  | 0 | for my $s (@current_day) { | 
| 70 | 0 | 0 |  |  |  | 0 | if ( $s->same_tags($e) ) { | 
| 71 | 0 |  |  |  |  | 0 | $do_merge = 1; | 
| 72 | 0 |  |  |  |  | 0 | $mergand  = $s; | 
| 73 | 0 |  |  |  |  | 0 | last; | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  | } | 
| 77 | 124 |  |  |  |  | 154 | when (MERGE_ADJACENT_SAME_TAGS) { | 
| 78 | 124 |  | 33 |  |  | 260 | $do_merge = $previous->adjacent($e) | 
| 79 |  |  |  |  |  |  | && $previous->same_tags($e) | 
| 80 |  |  |  |  |  |  | } | 
| 81 | 0 |  |  |  |  | 0 | when (MERGE_NONE) { $do_merge = 0 } | 
|  | 0 |  |  |  |  | 0 |  | 
| 82 | 0 |  |  |  |  | 0 | default { carp 'unfamiliar merge level' }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | # keep vacation and regular events apart | 
| 87 | 200 |  | 66 |  |  | 559 | $do_merge &&= ref $mergand->last_event eq ref $e; | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 200 | 100 |  |  |  | 425 | if ($do_merge) { | 
| 90 | 124 |  |  |  |  | 240 | $mergand->merge($e); | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | else { | 
| 93 | 76 |  |  |  |  | 237 | $previous = _new( $e, $merge_level ); | 
| 94 | 76 |  |  |  |  | 208 | push @synopses,    $previous; | 
| 95 | 76 |  |  |  |  | 207 | push @current_day, $previous; | 
| 96 |  |  |  |  |  |  | } | 
| 97 |  |  |  |  |  |  | } | 
| 98 | 8000 |  |  |  |  | 43250 | $day->{synopses} = \@synopses; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | # test to make sure this and the given event | 
| 102 |  |  |  |  |  |  | sub same_tags { | 
| 103 | 124 |  |  | 124 | 0 | 8798 | my ( $self, $event ) = @_; | 
| 104 | 124 |  |  |  |  | 239 | for my $e ( $self->events ) { | 
| 105 |  |  |  |  |  |  | return 0 | 
| 106 | 217 |  |  |  |  | 519 | unless $e->all_tags( @{ $event->tags } ) | 
| 107 | 217 | 50 | 33 |  |  | 246 | && $event->all_tags( @{ $e->tags } ); | 
|  | 217 |  |  |  |  | 537 |  | 
| 108 |  |  |  |  |  |  | } | 
| 109 | 124 |  |  |  |  | 518 | return 1; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | sub same_day { | 
| 113 | 0 |  |  | 0 | 0 | 0 | my ( $self, $event ) = @_; | 
| 114 | 0 |  |  |  |  | 0 | my $d1 = ( $self->events )[-1]->end; | 
| 115 | 0 |  |  |  |  | 0 | my $d2 = $event->start; | 
| 116 |  |  |  |  |  |  | return | 
| 117 | 0 |  | 0 |  |  | 0 | $d1->day == $d2->day | 
| 118 |  |  |  |  |  |  | && $d1->month == $d2->month | 
| 119 |  |  |  |  |  |  | && $d1->year == $d2->year; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | # whether given event is immediately adjacent to last event in synopsis | 
| 123 |  |  |  |  |  |  | sub adjacent { | 
| 124 | 124 |  |  | 124 | 0 | 160 | my ( $self, $event ) = @_; | 
| 125 | 124 | 100 |  |  |  | 343 | return 1 if !$event->can('end');    # notes are always considered adjacent | 
| 126 | 93 |  | 33 |  |  | 948 | my $d1 = ( $self->events )[-1]->end || now; | 
| 127 | 93 |  |  |  |  | 3433 | my $d2 = $event->start; | 
| 128 | 93 |  |  |  |  | 283 | return DateTime->compare( $d1, $d2 ) == 0; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | # add an event to the events described | 
| 132 | 124 |  |  | 124 | 0 | 128 | sub merge { push @{ $_[0]{events} }, $_[1] } | 
|  | 124 |  |  |  |  | 386 |  | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 0 |  |  | 0 | 1 | 0 | sub date { $_[0]->{events}[0]->start } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub description { | 
| 139 | 76 |  |  | 76 | 1 | 131 | my ($self) = @_; | 
| 140 | 76 | 50 |  |  |  | 205 | unless ( exists $self->{description} ) { | 
| 141 | 76 |  |  |  |  | 86 | my ( %seen, @descriptions ); | 
| 142 | 76 |  |  |  |  | 160 | for my $e ( $self->events ) { | 
| 143 | 200 |  |  |  |  | 218 | for my $d ( @{ $e->data->description } ) { | 
|  | 200 |  |  |  |  | 523 |  | 
| 144 | 200 | 50 |  |  |  | 583 | unless ( $seen{$d} ) { | 
| 145 | 200 |  |  |  |  | 506 | $seen{$d} = 1; | 
| 146 | 200 |  |  |  |  | 287 | chomp $d;    # got newline from log | 
| 147 | 200 |  |  |  |  | 531 | push @descriptions, $d; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  | } | 
| 151 | 76 |  |  |  |  | 191 | my $s = $descriptions[0]; | 
| 152 | 76 |  |  |  |  | 195 | for my $d ( @descriptions[ 1 .. $#descriptions ] ) { | 
| 153 | 124 | 50 |  |  |  | 478 | $s .= $s =~ /\w$/ ? '; ' : ' '; | 
| 154 | 124 |  |  |  |  | 257 | $s .= $d; | 
| 155 |  |  |  |  |  |  | } | 
| 156 | 76 |  |  |  |  | 350 | $self->{description} = $s; | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 76 |  |  |  |  | 286 | return $self->{description}; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | sub tags { | 
| 163 | 76 |  |  | 76 | 1 | 118 | my ($self) = @_; | 
| 164 | 76 |  |  |  |  | 96 | my %seen; | 
| 165 | 76 |  |  |  |  | 160 | my $s = ''; | 
| 166 | 76 |  |  |  |  | 247 | for my $e ( $self->events ) { | 
| 167 | 200 |  |  |  |  | 231 | for my $t ( @{ $e->tags } ) { | 
|  | 200 |  |  |  |  | 580 |  | 
| 168 | 12 |  |  |  |  | 146 | $seen{$t} = 1; | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 | 76 |  |  |  |  | 442 | return ( sort keys %seen ); | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | sub tag_string { | 
| 176 | 87 |  |  | 87 | 1 | 157 | my ($self) = @_; | 
| 177 |  |  |  |  |  |  | $self->{tag_string} = join ', ', $self->tags | 
| 178 | 87 | 100 |  |  |  | 371 | unless exists $self->{tag_string}; | 
| 179 | 87 |  |  |  |  | 322 | return $self->{tag_string}; | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 733 |  |  | 733 | 1 | 888 | sub events { @{ $_[0]->{events} } } | 
|  | 733 |  |  |  |  | 2358 |  | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 124 |  |  | 124 | 1 | 248 | sub last_event { ( $_[0]->events )[-1] } | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # constructs a single-event synopsis | 
| 189 |  |  |  |  |  |  | # NOTE: not a package method | 
| 190 |  |  |  |  |  |  | sub _new { | 
| 191 | 76 |  |  | 76 |  | 157 | my ( $event, $merge_level ) = @_; | 
| 192 | 76 | 50 | 33 |  |  | 378 | carp 'requires event argument' | 
| 193 |  |  |  |  |  |  | unless $event && $event->isa('App::JobLog::Log::Note'); | 
| 194 | 76 |  |  |  |  | 1300 | my ( $one_interval, $one_day ); | 
| 195 | 76 |  |  |  |  | 176 | for ($merge_level) { | 
| 196 | 76 |  |  |  |  | 138 | when (MERGE_ALL)      { ( $one_interval, $one_day ) = ( 0, 0 ) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 197 | 76 |  |  |  |  | 152 | when (MERGE_ADJACENT) { ( $one_interval, $one_day ) = ( 1, 1 ) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 198 | 76 |  |  |  |  | 142 | when (MERGE_ADJACENT_SAME_TAGS) { | 
| 199 | 65 |  |  |  |  | 136 | ( $one_interval, $one_day ) = ( 1, 1 ) | 
| 200 |  |  |  |  |  |  | } | 
| 201 | 11 |  |  |  |  | 35 | when (MERGE_SAME_TAGS) { ( $one_interval, $one_day ) = ( 0, 0 ) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 202 | 11 |  |  |  |  | 61 | when (MERGE_SAME_DAY)  { ( $one_interval, $one_day ) = ( 0, 1 ) } | 
|  | 0 |  |  |  |  | 0 |  | 
| 203 | 11 |  |  |  |  | 37 | when (MERGE_SAME_DAY_SAME_TAGS) { | 
| 204 | 0 |  |  |  |  | 0 | ( $one_interval, $one_day ) = ( 0, 1 ) | 
| 205 |  |  |  |  |  |  | } | 
| 206 | 11 |  |  |  |  | 50 | when (MERGE_NONE) { ( $one_interval, $one_day ) = ( 1, 1 ) } | 
|  | 11 |  |  |  |  | 81 |  | 
| 207 |  |  |  |  |  |  | } | 
| 208 | 76 |  |  |  |  | 766 | return bless { | 
| 209 |  |  |  |  |  |  | events       => [$event], | 
| 210 |  |  |  |  |  |  | one_interval => $one_interval, | 
| 211 |  |  |  |  |  |  | one_day      => $one_day | 
| 212 |  |  |  |  |  |  | }, | 
| 213 |  |  |  |  |  |  | __PACKAGE__; | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  |  | 
| 217 | 88 |  |  | 88 | 1 | 234 | sub single_interval { $_[0]->{one_interval} } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  |  | 
| 220 | 0 |  |  | 0 | 1 | 0 | sub single_day { $_[0]->{one_day} } | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | sub duration { | 
| 224 | 88 |  |  | 88 | 1 | 152 | my ($self) = @_; | 
| 225 | 88 |  |  |  |  | 180 | my @events = $self->events; | 
| 226 | 88 | 50 |  |  |  | 331 | return 0 unless $events[0]->can('end');    # notes have no duration | 
| 227 | 88 | 50 |  |  |  | 1151 | if ( $self->single_interval ) { | 
| 228 | 88 |  |  |  |  | 176 | my ( $se, $ee ) = ( $events[0], $events[$#events] ); | 
| 229 | 88 |  | 66 |  |  | 265 | my ( $start, $end ) = ( $se->start, $ee->end || now ); | 
| 230 | 88 |  |  |  |  | 3760 | return $end->epoch - $start->epoch; | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | else { | 
| 233 | 0 |  |  |  |  | 0 | my $d = 0; | 
| 234 | 0 |  |  |  |  | 0 | $d += $_->duration for @events; | 
| 235 | 0 |  |  |  |  | 0 | return $d; | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | sub time_fmt { | 
| 241 | 152 |  |  | 152 | 1 | 252 | my ($self) = @_; | 
| 242 | 152 |  |  |  |  | 326 | my @events = $self->events; | 
| 243 | 152 |  |  |  |  | 322 | my ( $se, $ee ) = ( $events[0], $events[$#events] ); | 
| 244 | 152 | 100 | 100 |  |  | 611 | if ( @events == 1 && !$se->can('end') ) {    # single note | 
| 245 | 2 |  |  |  |  | 30 | return $se->start->strftime('%l:%M %P'); | 
| 246 |  |  |  |  |  |  | } | 
| 247 | 150 | 100 |  |  |  | 1118 | my ( $start, $end ) = | 
| 248 |  |  |  |  |  |  | ( $se->start, $ee->can('end') ? $ee->end : $ee->start ); | 
| 249 | 150 |  |  |  |  | 265 | my $s; | 
| 250 | 150 | 100 |  |  |  | 504 | if ($end) { | 
| 251 | 138 | 50 | 33 |  |  | 5620 | return 'vacation' | 
| 252 |  |  |  |  |  |  | if ref $se eq 'App::JobLog::Vacation::Period' && !$se->fixed; | 
| 253 | 138 |  | 66 |  |  | 396 | my $same_period = $start->hour < 12 && $end->hour < 12 | 
| 254 |  |  |  |  |  |  | || $start->hour >= 12 && $end->hour >= 12; | 
| 255 | 138 | 100 | 66 |  |  | 2535 | if (   $same_period | 
|  |  |  | 100 |  |  |  |  | 
| 256 |  |  |  |  |  |  | && $start->hour == $end->hour | 
| 257 |  |  |  |  |  |  | && $start->minute == $end->minute ) | 
| 258 |  |  |  |  |  |  | { | 
| 259 | 62 |  |  |  |  | 1091 | $s = $start->strftime('%l:%M %P'); | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  | else { | 
| 262 | 76 | 100 |  |  |  | 536 | my ( $f1, $f2 ) = | 
| 263 |  |  |  |  |  |  | ( $same_period ? '%l:%M' : '%l:%M %P', '%l:%M %P' ); | 
| 264 | 76 |  |  |  |  | 272 | $s = $start->strftime($f1) . ' - ' . $end->strftime($f2); | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  | else { | 
| 268 | 12 |  |  |  |  | 154 | $s = $start->strftime('%l:%M %P') . ' - ongoing'; | 
| 269 |  |  |  |  |  |  | } | 
| 270 | 150 |  |  |  |  | 12777 | $s =~ s/  / /;    # strftime tends to add in an extra space | 
| 271 | 150 |  |  |  |  | 466 | $s =~ s/^ //; | 
| 272 | 150 |  |  |  |  | 640 | return $s; | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | 1; | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | __END__ |