File Coverage

blib/lib/E2/UserSearch.pm
Criterion Covered Total %
statement 18 188 9.5
branch 0 100 0.0
condition 0 76 0.0
subroutine 6 35 17.1
pod 5 18 27.7
total 29 417 6.9


line stmt bran cond sub pod time code
1             # E2::UserSearch
2             # Jose M. Weeks
3             # 06 July 2003
4             #
5             # See bottom for pod documentation.
6              
7             package E2::UserSearch;
8              
9 1     1   725 use 5.006;
  1         4  
  1         41  
10 1     1   5 use strict;
  1         1  
  1         28  
11 1     1   5 use warnings;
  1         2  
  1         23  
12 1     1   5 use Carp;
  1         2  
  1         55  
13              
14 1     1   5 use E2::Ticker;
  1         2  
  1         27  
15 1     1   10 use E2::Writeup;
  1         2  
  1         7046  
16              
17             our $VERSION = "0.33";
18             our @ISA = qw(E2::Ticker);
19             our $DEBUG; *DEBUG = *E2::Interface::DEBUG;
20              
21             sub new;
22             sub clear;
23              
24             sub writeups;
25             sub sort_results;
26              
27             sub new {
28 0     0 1   my $arg = shift;
29 0   0       my $class = ref( $arg ) || $arg;
30 0           my $self = $class->SUPER::new();
31              
32 0           bless ($self, $class);
33              
34 0           $self->clear;
35              
36 0           return $self;
37             }
38              
39             sub clear {
40 0 0   0 0   my $self = shift or croak "Usage: clear E2USERSEARCH";
41              
42 0 0         warn "E2::UserSearch::clear\n" if $DEBUG > 1;
43              
44 0           $self->{lastuser} = undef; # username of last user searched
45 0           @{ $self->{writeups} } = (); # list of E2::Writeup
  0            
46              
47 0           return 1;
48             }
49              
50             sub writeups {
51 0 0   0 1   my $self = shift or croak "Usage: writeups E2USERSEARCH [ USER ] [, SORT_BY ] [, COUNT ] [, STARTAT ]";
52 0   0       my $user = shift || $self->this_username;
53 0           my $sort_by = shift;
54 0           my $count = shift;
55 0           my $startat = shift;
56              
57 0 0         warn "E2::UserSearch::writeups\n" if $DEBUG > 1;
58            
59 0 0         if( !$user ) {
60 0 0         warn "No user specified and not logged in" if $DEBUG;
61 0           return undef;
62             }
63              
64 0           my %opt;
65              
66 0           $opt{searchuser} = $user;
67 0 0         $opt{startat} = $startat if $startat;
68              
69 0 0         if( $sort_by ) {
70 0           $sort_by = lc($sort_by);
71 0 0 0       if( $sort_by ne 'rep' && $sort_by ne 'creation' &&
      0        
72             $sort_by ne 'title' ) {
73 0           croak "Invalid search option: $sort_by";
74             }
75              
76 0           $opt{sort} = $sort_by;
77             } else {
78 0           $opt{nosort} = 1;
79 0           $sort_by = "none";
80             }
81              
82 0 0 0       if( $count && $count == -1 ) { # Get all
    0          
83 0           $opt{nolimit} = 1;
84             } elsif( $count ) {
85 0           $opt{count} = $count;
86             }
87              
88 0           $user = lc( $user );
89              
90             # We don't add this search to the last if this is a
91             # search on a new user.
92              
93 0 0 0       if( $self->{lastuser} && $self->{lastuser} ne $user ) {
94 0           $self->clear;
95             }
96              
97             # Ugly stuff, but this keeps our place so we
98             # can determine the rep-based order of
99             # writeups across multiple search loads.
100              
101 0   0       $self->{rep_number} = 100000 - ($startat || 0);
102              
103             my $handlers = {
104             'wu' => sub {
105 0     0     (my $a, my $b) = @_;
106 0           my $wu = new E2::Writeup;
107              
108 0           $wu->{type} = 'writeup';
109              
110 0           $wu->{createtime} = $b->{att}->{createtime};
111 0           $wu->{marked} = $b->{att}->{marked};
112 0           $wu->{hidden} = $b->{att}->{hidden};
113 0           $wu->{wrtype} = $b->{att}->{wrtype};
114              
115 0           $wu->{cool_count} = $b->{att}->{cools};
116              
117 0 0         if( my $rep = $b->first_child('rep') ) {
118 0           $wu->{rep}->{up} = $rep->{att}->{up};
119 0           $wu->{rep}->{down} = $rep->{att}->{down};
120 0           $wu->{rep}->{total} = $rep->text;
121             }
122            
123 0 0         if( my $lnk = $b->first_child( 'e2link' ) ) {
124 0           $wu->{title} = $lnk->text;
125 0           $wu->{node_id} = $lnk->{att}->{node_id};
126             }
127              
128 0 0         if( my $parent = $b->first_child( 'parent' ) ) {
129 0           my $l = $parent->first_child('e2link');
130 0           $wu->{parent} = $l->text;
131 0           $wu->{parent_id} = $l->{att}->{node_id};
132             }
133              
134             # We're going to add a value to the E2::Writeup.
135             # This is sort of a kludgy thing to do, but
136             # the situation (having to infer reputation
137             # based upon context) means we've got to store
138             # it somewhere.
139              
140 0 0         if( $sort_by eq 'rep' ) {
141 0           $wu->{_rep_position} = $self->{rep_number}--;
142             } else {
143 0           $wu->{_rep_position} = 0;
144             }
145              
146 0           push @{ $self->{writeups} }, $wu;
  0            
147             }
148 0           };
149              
150 0           @{$self->{writeups}} = (); # clear
  0            
151              
152 0           return $self->parse(
153             'usersearch',
154             $handlers,
155             $self->{writeups},
156             %opt
157             );
158             }
159              
160             sub sort_results {
161 0 0   0 1   my $self = shift or croak "Usage: sort_results E2USERSEARCH [, SORTBY [ , COUNT [ , STARTAT ] ] ]";
162 0           my $sortby = shift;
163 0           my $count = shift;
164 0           my $startat = shift;
165              
166 0           my $sort;
167              
168 0 0         warn "E2::UserSearch::sort_results\n" if $DEBUG > 1;
169              
170             # Define a bunch of sort routines
171             # (This whole thing is a mess..... ugly...... but it works)
172              
173             sub sort_by_creation {
174 0     0 0   $b->{createtime} =~ /(....)-(..)-(..) (..):(..):(..)/;
175 0           (my $year1, my $month1, my $day1, my $hour1, my $min1, my $sec1 )
176             = ($1, $2, $3, $4, $5, $6);
177 0           $a->{createtime} =~ /(....)-(..)-(..) (..):(..):(..)/;
178 0           (my $year2, my $month2, my $day2, my $hour2, my $min2, my $sec2 )
179             = ($1, $2, $3, $4, $5, $6);
180              
181 0 0 0       $year1 <=> $year2 || $month1 <=> $month2 || $day1 <=> $day2 ||
      0        
      0        
      0        
182             $hour1 <=> $hour2 || $min1 <=> $min2 || $sec1 <=> $sec2;
183             };
184              
185             sub sort_by_creation_reverse {
186 0     0 0   $a->{createtime} =~ /(....)-(..)-(..) (..):(..):(..)/;
187 0           (my $year1, my $month1, my $day1, my $hour1, my $min1, my $sec1 )
188             = ($1, $2, $3, $4, $5, $6);
189 0           $b->{createtime} =~ /(....)-(..)-(..) (..):(..):(..)/;
190 0           (my $year2, my $month2, my $day2, my $hour2, my $min2, my $sec2 )
191             = ($1, $2, $3, $4, $5, $6);
192              
193 0 0 0       $year1 <=> $year2 || $month1 <=> $month2 || $day1 <=> $day2 ||
      0        
      0        
      0        
194             $hour1 <=> $hour2 || $min1 <=> $min2 || $sec1 <=> $sec2;
195             };
196              
197 0     0 0   sub sort_by_title { $a->title cmp $b->title };
198              
199 0     0 0   sub sort_by_title_reverse { $b->title cmp $a->title };
200              
201 0   0 0 0   sub sort_by_rep { ($b->rep->{total} || 0) <=> ($a->rep->{total} || 0) };
      0        
202              
203 0     0 0   sub sort_by_rep_position { $b->{_rep_position} <=> $a->{_rep_position} };
204              
205 0     0 0   sub sort_by_rep_position_reverse { $a->{_rep_position} <=> $b->{_rep_position} };
206              
207 0   0 0 0   sub sort_by_rep_reverse { ($a->rep->{total} || 0) <=> ($b->rep->{total} || 0) };
      0        
208              
209 0     0 0   sub sort_by_cools { $b->cool_count <=> $a->cool_count };
210              
211 0     0 0   sub sort_by_cools_reverse { $a->cools <=> $a->cools };
212              
213 0     0 0   sub sort_by_random{ int(rand(3))-1 };
214            
215 0 0         if( !$count ) { $count = -1; }
  0            
216 0 0         if( !$startat ) { $startat = 0; }
  0            
217              
218             # Determine which way we want to sort and stick the method
219             # into the subroutine $sort.
220              
221 0 0         if( ! defined $sortby ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
222 0     0     $sort = sub { sort_by_creation; }
223 0           } elsif( ref( $sortby ) eq 'CODE' ) {
224 0           $sort = $sortby;
225             } elsif( lc($sortby) eq "creation" ) {
226 0     0     $sort = sub { sort_by_creation; }
227 0           } elsif( lc($sortby) eq "title" ) {
228 0     0     $sort = sub { sort_by_title; }
229 0           } elsif( lc($sortby) eq "rep" ) {
230 0 0 0 0     $sort = sub { sort_by_rep || sort_by_rep_position || sort_by_creation; }
231 0           } elsif( lc($sortby) eq "cools" ) {
232 0 0 0 0     $sort = sub { sort_by_cools || sort_by_rep || sort_by_rep_position; }
233 0           } elsif( lc($sortby) eq "creation_reverse" ) {
234 0     0     $sort = sub { sort_by_creation_reverse; }
235 0           } elsif( lc($sortby) eq "title_reverse" ) {
236 0     0     $sort = sub { sort_by_title_reverse; }
237 0           } elsif( lc($sortby) eq "rep_reverse" ) {
238 0 0 0 0     $sort = sub { sort_by_rep_reverse || sort_by_rep_position_reverse ||
239             sort_by_creation_reverse; }
240 0           } elsif( lc($sortby) eq "cools_reverse" ) {
241 0 0 0 0     $sort = sub { sort_by_cools_reverse || sort_by_rep_reverse ||
242             sort_by_rep_position_reverse; }
243 0           } elsif( lc($sortby) eq "random" ) {
244 0     0     $sort = sub { sort_by_random; }
245 0           } else {
246 0           croak "Invalid sort type: $sortby";
247             }
248              
249             # Sort
250              
251 0           my @sorted = sort $sort @{ $self->{writeups} };
  0            
252              
253 0 0         if( $count == -1 ) { return @sorted; }
  0            
254              
255 0           return splice @sorted, $startat, $count;
256             }
257              
258             sub compare {
259 0 0   0 1   my $self = shift or croak "Usage: compare E2USERSEARCH, OLDUSERSEARCH";
260 0 0         my $old = shift or croak "Usage: compare E2USERSEARCH, OLDUSERSEARCH";
261              
262 0 0         warn "E2::UserSearch::compare\n" if $DEBUG > 1;
263              
264 0 0 0       if( ! $self->{writeups} || ! $old->{writeups} ) {
265 0 0         warn"Usersearch not loaded" if $DEBUG;
266 0           return undef;
267             }
268              
269 0           my $stats;
270             my @changes;
271              
272             # Build a mapping of node_id to node for $old
273              
274 0           my %map;
275              
276 0           foreach( @{$old->{writeups}} ) {
  0            
277 0           $map{$_->node_id} = $_;
278             }
279              
280 0           foreach( $self->sort_results( 'rep' ) ) {
281              
282 0           my $writeup = {
283             title => $_->title,
284             node_id => $_->node_id,
285             parent => $_->parent,
286             parent_id => $_->parent_id,
287             rep => $_->rep,
288             cools => $_->cool_count
289             };
290              
291             # Get stats
292              
293 0           my $r = $_->rep->{total};
294              
295 0 0 0       if( !defined $stats->{min_rep} || $r < $stats->{min_rep} ) {
296 0           $stats->{min_rep} = $r;
297             }
298              
299 0 0 0       if( !defined $stats->{max_rep} || $r > $stats->{max_rep} ) {
300 0           $stats->{max_rep} = $r;
301             }
302              
303             # Store statistics
304              
305 0           $stats->{total_rep} += $r;
306 0           $stats->{total_cools} += $_->cool_count;
307 0           $stats->{$_->wrtype} += 1;
308              
309             # Get changes
310              
311 0           my $id = $_->node_id;
312 0           my $changed = undef;
313            
314 0 0         if( !$map{$id} ) { # New writeup (not yet stored)
315 0           $writeup->{new} = 1;
316 0           $writeup->{change_up} = $_->rep->{up};
317 0           $writeup->{change_down} = $_->rep->{down};
318 0           $writeup->{change_cools} = $_->cool_count;
319              
320 0           $changed = 1;
321              
322             } else {
323            
324             sub wr_diff {
325 0     0 0   my ($a, $b) = @_;
326 0   0       return $a->rep->{up} != $b->rep->{up} ||
327             $a->rep->{down} != $b->rep->{down} ||
328             $a->cool_count != $b->cool_count;
329             }
330              
331 0 0         if( wr_diff( $_, $map{$id} ) ) {
332            
333 0           $writeup->{change_up} = $_->rep->{up} -
334             $map{$id}->rep->{up};
335 0           $writeup->{change_down} = $_->rep->{down} -
336             $map{$id}->rep->{down};
337 0           $writeup->{change_cools} = $_->cool_count -
338             $map{$id}->cool_count;
339              
340 0           $changed = 1;
341             }
342              
343 0 0         if( $_->title ne $map{$id}->title ) {
344 0           $writeup->{old_title} = $map{$id}->title;
345            
346 0           $changed = 1;
347             }
348              
349 0           delete $map{$id};
350             }
351            
352 0 0         push @changes, $writeup if $changed;
353             }
354              
355             # Now store removed writeups
356              
357 0           foreach( keys %map ) {
358 0           push @changes, {
359             title => $_->title,
360             rep => $_->rep,
361             cools => $_->cools,
362             removed => 1
363             }
364             }
365            
366             # Store
367              
368 0           $self->{stats} = $stats;
369              
370 0           return @changes;
371             }
372              
373             sub stats {
374 0 0   0 1   my $self = shift or croak "Usage: stats E2USERSEARCH";
375              
376 0           return $self->{stats};
377             }
378              
379             1;
380             __END__