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] changelog2010-01-05 11:58 pm

[dw-free] Implement a feature similar to popwithfriends.bml

[commit: http://hg.dwscoalition.org/dw-free/rev/5e837b3c4787]

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

Add Popular Subscriptions feature, helps find new people to subscribe to.

Patch by [personal profile] yvi.

Files modified:
  • bin/upgrading/en.dat
  • cgi-bin/DW/Logic/MenuNav.pm
  • cgi-bin/LJ/User.pm
  • etc/config.pl
  • htdocs/tools/popsubscriptions.bml
  • htdocs/tools/popsubscriptions.bml.text
--------------------------------------------------------------------------------
diff -r f64ccc1de255 -r 5e837b3c4787 bin/upgrading/en.dat
--- a/bin/upgrading/en.dat	Tue Jan 05 22:53:19 2010 +0000
+++ b/bin/upgrading/en.dat	Tue Jan 05 23:58:20 2010 +0000
@@ -2097,6 +2097,8 @@ menunav.explore.interests=Interests
 
 menunav.explore.directorysearch=Directory Search
 
+menunav.explore.popsubscriptions=Popular Subscriptions
+
 menunav.explore.randomjournal=Random Journal
 
 menunav.explore.randomcommunity=Random Community
diff -r f64ccc1de255 -r 5e837b3c4787 cgi-bin/DW/Logic/MenuNav.pm
--- a/cgi-bin/DW/Logic/MenuNav.pm	Tue Jan 05 22:53:19 2010 +0000
+++ b/cgi-bin/DW/Logic/MenuNav.pm	Tue Jan 05 23:58:20 2010 +0000
@@ -55,6 +55,7 @@ sub get_menu_navigation {
     my $loggedin_canjoincomms = ( $loggedin && $u->is_person ) ? 1 : 0;   # note the semantic difference
     my $loggedin_hasnetwork = ( $loggedin && $u->can_use_network_page ) ? 1 : 0;
     my $loggedin_ispaid = ( $loggedin && $u->is_paid ) ? 1 : 0;
+    my $loggedin_popsubscriptions = ( $loggedin && $u->can_use_popsubscriptions );
     my $loggedout = $loggedin ? 0 : 1;
     my $always = 1;
     my $never = 0;
@@ -215,6 +216,11 @@ sub get_menu_navigation {
                     display => $always,
                 },
                 {
+                    url => "$LJ::SITEROOT/tools/popsubscriptions",
+                    text => "menunav.explore.popsubscriptions",
+                    display => $loggedin_popsubscriptions,
+                },
+                {
                     url => "$LJ::SITEROOT/support/faq",
                     text => "menunav.explore.faq",
                     display => $always,
diff -r f64ccc1de255 -r 5e837b3c4787 cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Tue Jan 05 22:53:19 2010 +0000
+++ b/cgi-bin/LJ/User.pm	Tue Jan 05 23:58:20 2010 +0000
@@ -1956,6 +1956,10 @@ sub can_use_mass_privacy {
     return $_[0]->get_cap( 'mass_privacy' ) ? 1 : 0;
 }
 
+sub can_use_popsubscriptions {
+    return $_[0]->get_cap( 'popsubscriptions' ) ? 1 : 0;
+}
+
 sub can_use_network_page {
     return 0 unless $_[0]->get_cap( 'friendsfriendsview' ) && $_[0]->is_person;
 }
diff -r f64ccc1de255 -r 5e837b3c4787 etc/config.pl
--- a/etc/config.pl	Tue Jan 05 22:53:19 2010 +0000
+++ b/etc/config.pl	Tue Jan 05 23:58:20 2010 +0000
@@ -365,7 +365,6 @@
             'findsim' => 0,
             'friendsfriendsview' => 0,
             'friendspage_per_day' => 0,
-            'friendspopwithfriends' => 0,
             'friendsviewupdate' => 0,
             'full_rss' => 1,
             'getselfemail' => 0,
@@ -378,6 +377,7 @@
             'mod_queue' => 50,
             'mod_queue_per_poster' => 3,
             'moodthemecreate' => 0,
+            popsubscriptions => 0,
             's2layersmax' => 0,
             's2props' => 0,
             's2styles' => 0,
@@ -421,7 +421,6 @@
             'findsim' => 1,
             'friendsfriendsview' => 1,
             'friendspage_per_day' => 1,
-            'friendspopwithfriends' => 1,
             'friendsviewupdate' => 1,
             'full_rss' => 1,
             'getselfemail' => 1,
@@ -434,6 +433,7 @@
             'mod_queue' => 100,
             'mod_queue_per_poster' => 5,
             'moodthemecreate' => 1,
+            popsubscriptions => 1,
             's2layersmax' => 150,
             's2props' => 1,
             's2styles' => 1,
@@ -475,7 +475,6 @@
             'findsim' => 1,
             'friendsfriendsview' => 1,
             'friendspage_per_day' => 1,
-            'friendspopwithfriends' => 1,
             'friendsviewupdate' => 1,
             'full_rss' => 1,
             'getselfemail' => 1,
@@ -488,6 +487,7 @@
             'mod_queue' => 100,
             'mod_queue_per_poster' => 5,
             'moodthemecreate' => 1,
+            popsubscriptions => 1,
             's2layersmax' => 300,
             's2props' => 1,
             's2styles' => 1,
@@ -536,7 +536,6 @@
             'findsim' => 1,
             'friendsfriendsview' => 1,
             'friendspage_per_day' => 1,
-            'friendspopwithfriends' => 1,
             'friendsviewupdate' => 1,
             'full_rss' => 1,
             'getselfemail' => 1,
@@ -549,6 +548,7 @@
             'mod_queue' => 100,
             'mod_queue_per_poster' => 5,
             'moodthemecreate' => 1,
+            popsubscriptions => 1,
             's2layersmax' => 300,
             's2props' => 1,
             's2styles' => 1,
@@ -588,7 +588,6 @@
             'findsim' => 1,
             'friendsfriendsview' => 1,
             'friendspage_per_day' => 1,
-            'friendspopwithfriends' => 1,
             'friendsviewupdate' => 1,
             'full_rss' => 1,
             'getselfemail' => 1,
@@ -601,6 +600,7 @@
             'mod_queue' => 100,
             'mod_queue_per_poster' => 5,
             'moodthemecreate' => 1,
+            popsubscriptions => 1,
             's2layersmax' => 300,
             's2props' => 1,
             's2styles' => 1,
diff -r f64ccc1de255 -r 5e837b3c4787 htdocs/tools/popsubscriptions.bml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/tools/popsubscriptions.bml	Tue Jan 05 23:58:20 2010 +0000
@@ -0,0 +1,203 @@
+<?_c
+#
+# tools/popsubscriptions.bml
+#
+# Page that shows a sorted list of popular accounts in the user's circle. User can pick
+# which circle group to base these calculations on by selecting from a drop-down menu.
+# Results are displayed in three sections: personal accounts, community accounts, feed accounts.
+# 
+# Authors:
+#   Rebecca Freiburg <beckyvi@gmail.com>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+_c?>
+
+<?page
+title=><?_ml .title _ml?>
+body<=
+<?_code
+{
+    use strict;
+    use warnings;
+
+    my $ret = "<?p $ML{'.intro'} p?>";
+
+    my $remote = LJ::get_remote();
+
+    return "<?needlogin?>" unless $remote;
+
+    return $ML{'.invalidaccounttype'} unless $remote->can_use_popsubscriptions;
+
+    return $ML{'.disabled'} unless LJ::is_enabled( 'popsubscriptions' );
+
+    # filters options: 
+    # 1: only accounts subscribed to
+    # 2: only mutually subscribed accounts
+    # 3: trusted accounts
+    # 4: mutually trusted accounts
+    # 5: whole circle => subscriptions + gives access to
+
+    my @calc_options;
+    @calc_options = ("1", $ML{'.filters.subscriptions'}, "2", $ML{'.filters.mutualsubscriptions'}, "3", $ML{'.filters.access'},
+        "4", $ML{'.filters.mutualaccess'}, "5", $ML{'.filters.circle'} );
+
+    # drop-down menu and button for selecting which user accounts to base the calulations on
+    $ret .= "<?p $ML{'.options.filter'} p?>";
+    $ret .= "<form method='get' action='/tools/popsubscriptions'>\n";
+    $ret .= LJ::html_select( { name => 'filter'}, @calc_options );
+    $ret .= LJ::html_submit( $ML{'.options.submit'} );
+    $ret .= "</form><br /></p>";
+
+    # circle_ids stores the user ids of accounts to base the calculation on. which accounts these are is based on which filter the user picks.
+    # default: subscriptions (filter: 1)
+    my @circle_ids;
+
+    my $filter = $GET{filter};
+    if ( !$filter || $filter == 1 ) {
+        @circle_ids = $remote->watched_userids;
+    } elsif ( $filter == 2 ) {
+        @circle_ids = $remote->mutually_watched_userids;
+    } elsif ( $filter == 3 ) {
+        @circle_ids = $remote->trusted_userids;
+    } elsif ( $filter == 4 ) {
+        @circle_ids = $remote->mutually_trusted_userids;
+    } else {
+        @circle_ids = $remote->circle_userids;
+    }
+
+    # circle can currently have a maximum of 4,000 userid (2,000 watched, 2,000 trusted) calculate for a maximum of 750 of  them
+    # on those scales, we won't lose much resolution doing this, as trusted and watched are almost always overlapping
+    my $circle_limit = 750;
+    @circle_ids = splice( @circle_ids, 0, $circle_limit ) if @circle_ids > $circle_limit;
+
+    # hash for searching whether the user is already subscribed to someone later
+    my %circle_members;
+    foreach my $id ( @circle_ids ) {
+        $circle_members{$id} = 1;
+    }
+
+    # hash for counting how many accounts in @circle_ids are subscribed to a particular account
+    my %count;
+
+    # limit the number of userids loaded for each @circle_ids user to 500
+    my $limit = 500;
+    my $remote_id = $remote->userid;
+
+    # load users...
+    my $circleusers = LJ::load_userids( @circle_ids );
+
+    # now load the accounts the users are watching...
+    foreach my $uid ( @circle_ids ) {
+        # don't want to include the user themself
+        next if $uid == $remote_id;
+
+        # since we have just loaded all user objects, we can now load subscribed accounts of that user
+        my $circleuser = $circleusers->{$uid};
+
+        # but we only include undeleted, unsuspended, personal journals (personal + identity)
+        next unless $circleuser->is_individual || ! $circleuser->is_inactive;
+
+        # get userids subscribers are watching
+        my @subsubs =$circleuser->watched_userids( limit => $limit ) ;
+
+        # if there are none, skip to next subscription
+        next unless @subsubs;
+
+        # now we count the occurance of the userids
+        foreach my $userid ( @subsubs ) {
+            $count{$userid}++;
+        }
+    }
+
+    # now that we have the count for all userids, we sort it and take the most popular 500
+    my @pop = sort { $count{$b} <=> $count{$a} } keys %count;
+    @pop =  splice( @pop, 0, 500 );
+
+    # now we sort according to personal, community or feed account and only take the top 50 accounts
+    # for this we need to lead the user objects
+    my $popusers = LJ::load_userids( @pop );
+    my ( @poppersonal, @popcomms, @popfeeds );
+    my ( $numberpersonal, $numbercomms, $numberfeeds ) = ( 0, 0, 0 );
+    my $maximum = 50;
+    foreach my $uid ( @pop ) {
+        my $popuser = $popusers->{$uid};
+
+        # don't show already subscribed to accounts, inactive accounts, or banned accounts
+        next if $circle_members{$uid} || $uid == $remote_id || $popuser->is_inactive || $remote->is_banned( $popuser, $remote);
+
+        # sort userids into arrays
+        if ( $numberpersonal < $maximum && $popuser->is_personal ) {
+            push @poppersonal, $uid;
+            $numberpersonal++;
+        } elsif ( $numbercomms < $maximum && $popuser->is_community ) {
+            push @popcomms, $uid;
+            $numbercomms++;
+        } elsif ( $numberfeeds < $maximum && $popuser->is_syndicated ) {
+            push @popfeeds, $uid;
+            $numberfeeds++;
+        }
+
+        # don't continue loop if all three arrays have reached the maximum number
+        last if $numberpersonal + $numbercomms + $numberfeeds >= $maximum * 3;
+    }
+
+    # function to construct table rows which we print later
+    sub make_results_rows {
+        my ( $poparray, $count ) = @_;
+        my ( $results, $count_user, $popularu );
+
+        # need to load user objects to use ->ljuser_display. this is for a maximum of 50 accounts here
+        my $popularusers = LJ::load_userids( @$poparray );
+
+        foreach my $popularid ( @$poparray ) {
+            $count_user = $count->{$popularid};
+            $popularu = $popularusers->{$popularid};
+            $results .= "<tr><td>" . $popularu->ljuser_display . "</td>";
+            $results .= "<td align='right'>&nbsp;$count_user</td></tr>";
+        }
+        return $results;        
+    }
+
+    # store results in table format. these rows can be empty
+    my ( $results_personal, $results_communities, $results_feeds );
+    $results_personal = make_results_rows ( \@poppersonal, \%count );
+    $results_communities = make_results_rows ( \@popcomms, \%count );
+    $results_feeds = make_results_rows ( \@popfeeds, \%count );
+
+    # no results to display: show error message
+    unless ( $results_personal || $results_communities || $results_feeds ) {
+        $ret .= $ML{'.error.noresults'};
+        return $ret;
+    }
+
+    # print the tables
+    # only show each result section if there are accounts in them, if not show a message
+    if ( $results_personal ) {
+        $ret .= $ML{'.results.personal'};
+        $ret .= "<table cellpadding='3'>" . $results_personal . "</table><br /></p>";
+    } else {
+        $ret .= "<?p $ML{'.error.noresults.personal'} p?>";
+    }
+    if ( $results_communities ) {
+        $ret .= $ML{'.results.communities'};
+        $ret .= "<table cellpadding='3'>" . $results_communities . "</table><br /></p>";
+    } else {
+        $ret .= "<?p $ML{'.error.noresults.communities'} p?>";
+    }
+    if ( $results_feeds) {
+        $ret .= $ML{'.results.feeds'};
+        $ret .= "<table cellpadding='3'>" . $results_feeds . "</table><br /></p>";
+    } else {
+        $ret .= "<?p $ML{'.error.noresults.feeds'} p?>";
+    }
+
+    return $ret;
+
+
+}
+_code?>
+<=body
+page?>
diff -r f64ccc1de255 -r 5e837b3c4787 htdocs/tools/popsubscriptions.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/tools/popsubscriptions.bml.text	Tue Jan 05 23:58:20 2010 +0000
@@ -0,0 +1,39 @@
+.communities.intro=These are the most popular community accounts in your circle:
+
+.disabled=This feature is not available on this service.
+
+.error.noresults=Sorry, there were no accounts found for your search.
+
+.error.noresults.communities=Sorry, there were no community accounts found for your search.
+
+.error.noresults.feeds=Sorry, there were no personal accounts found for your search.
+
+.error.noresults.personal=Sorry, there were no feeds found for your search.
+
+.feeds.intro=These are the most popular feeds in your circle:
+
+.filters.access=access list
+
+.filters.circle=your circle
+
+.filters.mutualaccess=mutual access
+
+.filters.mutualsubscriptions=mutual subscriptions
+
+.filters.subscriptions=subscriptions
+
+.intro=This page displays popular subscriptions within your circle. Only personal accounts are used for this analysis.
+
+.invalidaccounttype=Sorry, your account type does not permit using this page.
+
+.options.filter=Select a method to filter which users to include for this analysis.
+
+.options.submit=Submit
+
+.results.communities=These are the most popular community accounts in your circle:
+
+.results.feeds=These are the most popular feeds in your circle:
+
+.results.personal=These are the most popular personal accounts in your circle:
+
+.title=Popular With Circle
--------------------------------------------------------------------------------
sofiaviolet: drawing of three violets and three leaves (Default)

[personal profile] sofiaviolet 2010-01-06 02:00 am (UTC)(link)
:D Mah suggestion! It are implemented! \o/

Looking forward to this going live.
denise: Image: Me, facing away from camera, on top of the Castel Sant'Angelo in Rome (Default)

[staff profile] denise 2010-01-06 03:15 am (UTC)(link)
I think implementing suggestions is my favorite part of coding :)
sofiaviolet: drawing of three violets and three leaves (Default)

[personal profile] sofiaviolet 2010-01-06 03:43 am (UTC)(link)
I am going to promote this so hard once it gets pushed. *happydance*
yvi: Kaylee half-smiling, looking very pretty (Default)

[personal profile] yvi 2010-01-06 07:19 am (UTC)(link)
Whoo!