mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-09-30 06:22 am

[dw-free] birthday sort order needs fixing

[commit: http://hg.dwscoalition.org/dw-free/rev/422d252c7618]

http://bugs.dwscoalition.org/show_bug.cgi?id=1610

Fix birthday display logic to properly handle people who have already been
processed by the notifier.

Patch by [personal profile] kareila.

Files modified:
  • cgi-bin/DW/User/Edges/WatchTrust.pm
--------------------------------------------------------------------------------
diff -r 0732b0ee3f52 -r 422d252c7618 cgi-bin/DW/User/Edges/WatchTrust.pm
--- a/cgi-bin/DW/User/Edges/WatchTrust.pm	Wed Sep 30 05:45:45 2009 +0000
+++ b/cgi-bin/DW/User/Edges/WatchTrust.pm	Wed Sep 30 06:22:21 2009 +0000
@@ -338,72 +338,56 @@ sub trustmask {
 #      pass in full => 1 to get all friends' birthdays.
 # returns: arrayref of [ month, day, username ] arrayrefs
 sub get_birthdays {
+    my ( $u, %opts ) = @_;
+    $u = LJ::want_user( $u ) or return undef;
 
-    my $u = LJ::want_user( shift )
-        or return undef;
-
-    my %opts = @_;
     my $months_ahead = $opts{months_ahead} || 3;
     my $full = $opts{full};
 
-    # what day is it now?
-    my $now = $u->time_now;
-    my ($mnow, $dnow) = ($now->month, $now->day);
-
+    # try to get the cached birthday
     my $memkey = [$u->userid, 'bdays:' . $u->userid . ':' . ($full ? 'full' : $months_ahead)];
-    my $cached_bdays = LJ::MemCache::get($memkey);
-    # we cached the sorted data, don't need to re-sort
+    my $cached_bdays = LJ::MemCache::get( $memkey );
     return @$cached_bdays if $cached_bdays;
 
     my @circle = $u->is_community ? $u->member_userids : $u->circle_userids;
-    my $nb = LJ::User->next_birthdays( @circle );
-    # returns ref to hash of form (userid => date)
-    my $uids = LJ::load_userids( keys %$nb );
-    # returns ref to hash of form (userid => user object)
-    my %timedata;
+    my $nb = LJ::User->next_birthdays( @circle ) or return undef;
 
-    while ( my ( $id, $u ) = each %$uids ) {
+    # now map it to an array with DateTime objects
+    my @bdays = map { [ $_, DateTime->from_epoch( epoch => $nb->{$_} ) ] }
+                    keys %$nb;
+
+    # now push a second set of objects, for each object, one year ago
+    push @bdays, [ $bdays[$_]->[0],
+                   $bdays[$_]->[1]->clone->subtract( years => 1 ) ]
+        for 0..$#bdays;
+
+    # sort the list... will end up nicely sorted by time
+    @bdays = sort { DateTime->compare( $a->[1], $b->[1] ) } @bdays;
+
+    # remove anything that is actually in the past
+    my $now = $u->time_now;
+    shift @bdays while @bdays && $bdays[0]->[1]->epoch < $now->epoch;
+
+    # remove anything that is too far in the future
+    my $months = $full ? 12 : $months_ahead;
+    my $compare = $now->add( months => $months )->epoch;
+    pop @bdays while @bdays && $bdays[-1]->[1]->epoch > $compare;
+
+    # now load the userids
+    my $uids = LJ::load_userids( map { $_->[0] } @bdays );
+
+    my @results;
+    foreach my $ub ( @bdays ) {
+        my $u = $uids->{$ub->[0]};
         next unless $u->is_personal && $u->can_show_bday;
-        my $date = $nb->{$id} or next;
 
-        # need numeric month and day
-        my @lt = localtime( $date );
-        my $month = $lt[4] + 1;
-        my $day = $lt[3];
-        next unless $month > 0 && $day > 0;
-
-        # skip over unless a few months away (except in full mode)
-        unless ($full) {
-            # the case where months_ahead doesn't wrap around to a new year
-            if ($mnow + $months_ahead <= 12) {
-                # discard old months
-                next if $month < $mnow;
-                # discard months too far in the future
-                next if $month > $mnow + $months_ahead;
-
-            # the case where we wrap around the end of the year (eg, oct->jan)
-            } else {
-                # we're okay if the month is in the future, because
-                # we KNOW we're wrapping around. but if the month is
-                # in the past, we need to verify that we've wrapped
-                # around and are still within the timeframe
-                next if ($month < $mnow) && ($month > ($mnow + $months_ahead) % 12);
-            }
-
-            # month is fine. check the day.
-            next if ($month == $mnow && $day < $dnow);
-        }
-
-        $timedata{"$date.$id"} = [$month, $day, $u->user];
+        push @results, [ $ub->[1]->month, $ub->[1]->day, $u->user ];
     }
 
-    # hash slice for array sorted by date
-    my @bdays = @timedata{ sort keys %timedata };
+    # set birthdays in memcache for later
+    LJ::MemCache::set( $memkey, \@results, 86400 );
 
-    # set birthdays in memcache for later
-    LJ::MemCache::set($memkey, \@bdays, 86400);
-
-    return @bdays;
+    return @results;
 }
 *LJ::User::get_birthdays = \&get_birthdays;
 
--------------------------------------------------------------------------------