Skip to content

Commit

Permalink
New insight: Twitter Birthday
Browse files Browse the repository at this point in the history
Each year on your anniversary of joining Twitter, gives you an insight.
Shows people you follow that joined just before and after you.
Added getFollowersJoinedInTimeFrame and countTotalFriendsJoinedAfterDate to FollowDAO

closes #1514, closes #1956
  • Loading branch information
cdmoyer authored and ginatrapani committed May 29, 2014
1 parent a32e852 commit e511b74
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 6 deletions.
40 changes: 38 additions & 2 deletions tests/TestOfFollowMySQLDAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,28 @@ protected function buildData() {
//Insert test data into test table

$builders[] = FixtureBuilder::build('users', array('user_id'=>'1234567890', 'user_name'=>'jack',
'joined' => '2008-01-01',
'full_name'=>'Jack Dorsey', 'avatar'=>'avatar.jpg', 'follower_count'=>150210, 'friend_count'=>124,
'is_verified'=>1, 'is_protected'=>0, 'network'=>'twitter', 'description'=>'Square founder, Twitter creator'));

$builders[] = FixtureBuilder::build('users', array('user_id'=>'1324567890', 'user_name'=>'ev',
'joined' => '2009-01-01',
'full_name'=>'Ev Williams', 'avatar'=>'avatar.jpg', 'last_updated'=>'2005-01-01 13:58:25',
'follower_count'=>36000, 'is_protected'=>0, 'network'=>'twitter',
'description'=>'Former Googler, Twitter creator'));

$builders[] = FixtureBuilder::build('users', array('user_id'=>'1623457890', 'user_name'=>'private',
'joined' => '2010-01-01',
'full_name'=>'Private Poster', 'avatar'=>'avatar.jpg', 'is_protected'=>1, 'follower_count'=>35342,
'is_verified'=>0, 'friend_count'=>1345));

$builders[] = FixtureBuilder::build('users', array('user_id'=>'1723457890', 'user_name'=>'facebookuser1',
'joined' => '2011-01-01',
'full_name'=>'Facebook User 1', 'avatar'=>'avatar.jpg', 'is_protected'=>1, 'follower_count'=>35342,
'friend_count'=>1345, 'network'=>'facebook'));

$builders[] = FixtureBuilder::build('users', array('user_id'=>'1823457890', 'user_name'=>'facebookuser2',
'joined' => '2012-01-01',
'full_name'=>'Facebook User 2', 'avatar'=>'avatar.jpg', 'is_protected'=>1, 'follower_count'=>35342,
'friend_count'=>1345, 'network'=>'facebook'));

Expand Down Expand Up @@ -98,6 +103,11 @@ protected function buildData() {
$builders[] = FixtureBuilder::build('follows', array('user_id'=>'1723457890', 'follower_id'=>'1823457890',
'active'=>1, 'last_seen'=>'2006-01-08 23:54:41', 'network'=>'facebook'));

$builders[] = FixtureBuilder::build('follows', array('user_id'=>'1324567890', 'follower_id'=>'1',
'last_seen'=>'2006-01-08 23:54:41', 'network'=>'twitter'));
$builders[] = FixtureBuilder::build('follows', array('user_id'=>'1234567890', 'follower_id'=>'1',
'last_seen'=>'2006-01-08 23:54:41', 'network'=>'twitter'));

return $builders;
}

Expand Down Expand Up @@ -143,7 +153,7 @@ public function testGetUnloadedFollowerDetails() {
$unloaded_followers = $this->DAO->getUnloadedFollowerDetails(1324567890, 'twitter');

$this->assertIsA($unloaded_followers, "array");
$this->assertEqual(count($unloaded_followers), 2);
$this->assertEqual(count($unloaded_followers), 3);
$this->assertEqual($unloaded_followers[0]['follower_id'], 17);
$this->assertEqual($unloaded_followers[1]['follower_id'], 14);
}
Expand Down Expand Up @@ -485,4 +495,30 @@ public function testSearchFollowers(){
$this->assertEqual(count($result), 1);
$this->assertEqual($result[0]->full_name, "Jack Dorsey");
}
}

public function testGetFriendsJoinedInTimeFrame() {
$result = $this->DAO->getFriendsJoinedInTimeFrame(1234567890, 'twitter', '2008-02-01', '2010-02-01');
$this->assertEqual(1, count($result));
$this->assertEqual("ev", $result[0]->username);

$result = $this->DAO->getFriendsJoinedInTimeFrame(1, 'twitter', '2008-02-01', '2011-02-01');
$this->assertEqual(1, count($result));
$this->assertEqual("ev", $result[0]->username);

$result = $this->DAO->getFriendsJoinedInTimeFrame(1, 'twitter', '2006-02-01', '2011-02-01');
$this->assertEqual(2, count($result));
$this->assertEqual("jack", $result[0]->username);
$this->assertEqual("ev", $result[1]->username);
}

public function testCountTotalFriendsJoinedAfterDate() {
$result = $this->DAO->countTotalFriendsJoinedAfterDate(1234567890, 'twitter', '2009-02-01');
$this->assertEqual(1, $result);
$result = $this->DAO->countTotalFriendsJoinedAfterDate(1234567890, 'twitter', '2008-02-01');
$this->assertEqual(2, $result);
$result = $this->DAO->countTotalFriendsJoinedAfterDate(1234567890, 'twitter', '2018-02-01');
$this->assertEqual(0, $result);
$result = $this->DAO->countTotalFriendsJoinedAfterDate(1234567890, 'facebook', '1812-02-01');
$this->assertEqual(0, $result);
}
}
1 change: 1 addition & 0 deletions tests/all_plugin_tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
$plugin_tests->add(new TestOfCongratsCountInsight());
$plugin_tests->add(new TestOfTimeSpentInsight());
$plugin_tests->add(new TestOfTwitterAgeInsight());
$plugin_tests->add(new TestOfTwitterBirthdayInsight());
// One-time or developer insight tests that don't have to run every time
// $plugin_tests->add(new TestOfHelloThinkUpInsight());
// $plugin_tests->add(new TestOfOlympics2014Insight());
Expand Down
37 changes: 36 additions & 1 deletion webapp/_lib/dao/class.FollowMySQLDAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,22 @@ public function countTotalFriends($user_id, $network) {
return $this->getDataCountResult($ps);
}

public function countTotalFriendsJoinedAfterDate($user_id, $network, $date) {
$q = "SELECT count( * ) AS count FROM #prefix#follows AS f ";
$q .= "INNER JOIN #prefix#users u ON u.user_id = f.user_id ";
$q .= "WHERE f.follower_id = :user_id AND u.joined>:date AND u.network=:network AND f.network = u.network ";
$q .= "AND f.active=1";
$vars = array(
':user_id'=>(int)$user_id,
':network'=>$network,
':date'=>$date
);
if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); }
$ps = $this->execute($q, $vars);

return $this->getDataCountResult($ps);
}

public function countTotalFriendsProtected($user_id, $network) {
$q = "SELECT count( * ) AS count FROM #prefix#follows AS f ";
$q .= "INNER JOIN #prefix#users u ON u.user_id = f.user_id ";
Expand Down Expand Up @@ -677,4 +693,23 @@ public function searchFollowers(array $keywords, $network, $user_id, $page_numbe

return $this->getDataRowsAsObjects($ps, 'User');
}
}

public function getFriendsJoinedInTimeFrame($user_id, $network, $earliest_date, $latest_date) {
$q = "SELECT u.* ";
$q .= "FROM #prefix#users AS u ";
$q .= "INNER JOIN #prefix#follows f ON u.user_id = f.user_id ";
$q .= "WHERE f.follower_id = :user_id AND f.network=:network AND u.network=f.network AND active=1 ";
$q .= "AND (u.joined >= :early AND u.joined <= :late) AND u.is_protected=0 ";
$q .= "ORDER BY u.user_id ASC";
$vars = array(
':user_id'=>(string)$user_id,
':network'=>$network,
':early'=>$earliest_date,
':late'=>$latest_date
);
if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); }
$ps = $this->execute($q, $vars);

return $this->getDataRowsAsObjects($ps, 'User');
}
}
19 changes: 18 additions & 1 deletion webapp/_lib/dao/interface.FollowDAO.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,15 @@ public function countTotalFollowsProtected($user_id, $network);
*/
public function countTotalFriends($user_id, $network);
/**
* Gets the number of friends that is protected.
* Gets the number of friends of a user that joined the network after the specified date
* @param int $user_id
* @param str $network
* @param str $date Fetch friends joined after this date
* @return int Number of friends that joined after date
*/
public function countTotalFriendsJoinedAfterDate($user_id, $network, $date);
/**
* Gets the number of friends that are protected.
* Includes inactive friendships in count.
* @param int $user_id
* @param str $network
Expand Down Expand Up @@ -261,4 +269,13 @@ public function getFolloweesRepliedToThisWeekLastYear($user_id, $network);
* @param int $page_count
*/
public function searchFollowers(array $keywords, $network, $user_id, $page_number=1, $page_count=20);
/**
* Gets the followers that joined just before user
* @param str $user_id
* @param str $network
* @param str $earliest_date Earliest date a friend should have joined to be returned
* @param str $latest_date Latest date a friend should have joined to be returned
* @return array Array of User objects
**/
public function getFriendsJoinedInTimeFrame($user_id, $network, $earliest_date, $latest_date);
}
133 changes: 133 additions & 0 deletions webapp/plugins/insightsgenerator/insights/twitterbirthday.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php
/*
Plugin Name: Twitter Birthday
Description: A happy birthday notice and comparison to the join dates of the people you follow.
When: Yearly, on anniversary of joining Twitter
*/
/**
*
* ThinkUp/webapp/plugins/insightsgenerator/insights/twitterbirthday.php
*
* Copyright (c) 2014 Chris Moyer
*
* LICENSE:
*
* This file is part of ThinkUp (http://thinkup.com).
*
* ThinkUp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any
* later version.
*
* ThinkUp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with ThinkUp. If not, see
* <http://www.gnu.org/licenses/>.
*
* @license http://www.gnu.org/licenses/gpl.html
* @copyright 2014 Chris Moyer
* @author Chris Moyer <chris [at] inarow [dot] net>
*/
class TwitterBirthdayInsight extends InsightPluginParent implements InsightPlugin {
public function generateInsight(Instance $instance, User $user, $last_week_of_posts, $number_days) {
parent::generateInsight($instance, $user, $last_week_of_posts, $number_days);
$this->logger->logInfo("Begin generating insight", __METHOD__.','.__LINE__);

$joined_ts = strtotime($user->joined, TimeHelper::getTime());
$joined_day = date('m-d', $joined_ts);
$is_twitter = $instance->network == 'twitter';
if ($is_twitter && date('m-d', TimeHelper::getTime()) == $joined_day) {
$insight = new Insight();
$insight->slug = "twitterbirthday";
$insight->instance_id = $instance->id;
$insight->date = $this->insight_date;
$insight->filename = basename(__FILE__, ".php");
$insight->headline = "Happy Twitter birthday!";

$follow_dao = DAOFactory::getDAO('FollowDAO');
$all_friends = $follow_dao->countTotalFriends($instance->network_user_id, $instance->network);
$late_friends = $follow_dao->countTotalFriendsJoinedAfterDate($instance->network_user_id,
$instance->network, $user->joined);
$years = date('Y', TimeHelper::getTime()) - date('Y', $joined_ts);

$insight->text = $this->username." joined Twitter $years year".($years==1?'':'s')." ago today";

if ($all_friends > 0 && $late_friends > 0) {
$percent_before = floor($late_friends / $all_friends * 100);
$insight->text .= ", " . "before ".$percent_before."% of the people ".$this->username." follows did.";
}
else {
$insight->text .= ".";
}

$week_seconds = 60 * 60 * 24 * 7;
$followers = $follow_dao->getFriendsJoinedInTimeFrame($user->user_id, $instance->network,
date('Y-m-d', $joined_ts - $week_seconds), date('Y-m-d', $joined_ts + $week_seconds));

$just_before = null;
$just_after = null;

$last_user = null;
foreach ($followers as $follower) {
if (strtotime($follower->joined, TimeHelper::getTime()) > $joined_ts) {
$just_after = $follower;
$just_before = $last_user;
break;
}
$last_user = $follower;
}
if (!$just_after && $last_user) {
$just_before = $last_user;
}

$bonus_text = array();
$people = array();
if ($just_before) {
$time = $this->secondsToRelativeTime(abs($joined_ts - strtotime($just_before->joined,
TimeHelper::getTime())));
$bonus_text[] = "@".$just_before->username." just beat ".$this->username.", joining $time earlier";
$people[] = $just_before;
}
if ($just_after) {
$time = $this->secondsToRelativeTime(abs($joined_ts - strtotime($just_after->joined,
TimeHelper::getTime())));
$bonus_text[] = "@".$just_after->username." was a little slower, getting on Twitter $time later";
$people[] = $just_after;
}

if (count($bonus_text)) {
$insight->text .= " ".join(', and ', $bonus_text).".";
$insight->setPeople($people);
}

$res = $this->insight_dao->insertInsight($insight);
}

$this->logger->logInfo("Done generating insight", __METHOD__.','.__LINE__);
}

public function secondsToRelativeTime($seconds) {
if ($seconds >= (60*60*24*7)) {
$weeks = floor($seconds / (60*60*24*7));
return $weeks." week".($weeks==1?'':'s');
}
if ($seconds >= (60*60*24)) {
$days = floor($seconds / (60*60*24));
return $days." day".($days==1?'':'s');
}
if ($seconds >= (60*60)) {
$hours = floor($seconds / (60*60));
return $hours." hour".($hours==1?'':'s');
}
if ($seconds >= 60) {
$minutes = floor($seconds / 60);
return $minutes." minute".($minutes==1?'':'s');
}

return $seconds." second".($seconds==1?'':'s');
}
}

$insights_plugin_registrar = PluginRegistrarInsights::getInstance();
$insights_plugin_registrar->registerInsightPlugin('TwitterBirthdayInsight');
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,9 @@ public function testRandomizedDailySubjects() {
'email'=>'admin@example.com', 'is_activated'=>1, 'email_notification_frequency' => 'daily',
'timezone' => 'America/New_York'));
$builders[] = FixtureBuilder::build('instances', array('network_username'=>'cdmoyer', 'id' => 6,
'network'=>'twitter', 'is_activated'=>1, 'is_public'=>1));
'network'=>'twitter', 'is_activated'=>1, 'is_public'=>1, 'network_user_id'=>'11'));
$builders[] = FixtureBuilder::build('users', array('user_id' => '11', 'network' => 'twitter',
'joined' => date('Y-m-d', strtotime('-10 day'))));
$builders[] = FixtureBuilder::build('owner_instances', array('owner_id'=>1, 'instance_id'=>6, 'id'=>1));
$builders[] = FixtureBuilder::build('insights', array('id'=>2, 'instance_id'=>6,
'slug'=>'new_group_memberships', 'headline'=>'Made the List:',
Expand Down Expand Up @@ -807,7 +809,9 @@ public function testRandomizedDailySubjectsOneInsight() {
'email'=>'admin@example.com', 'is_activated'=>1, 'email_notification_frequency' => 'daily',
'timezone' => 'America/New_York'));
$builders[] = FixtureBuilder::build('instances', array('network_username'=>'cdmoyer', 'id' => 6,
'network'=>'twitter', 'is_activated'=>1, 'is_public'=>1));
'network'=>'twitter', 'is_activated'=>1, 'is_public'=>1, 'network_user_id'=>'11'));
$builders[] = FixtureBuilder::build('users', array('user_id' => '11', 'network' => 'twitter',
'joined' => date('Y-m-d', strtotime('-10 day'))));
$builders[] = FixtureBuilder::build('owner_instances', array('owner_id'=>1, 'instance_id'=>6, 'id'=>1));
$builders[] = FixtureBuilder::build('options', array('namespace'=>'application_options',
'option_name'=>'server_name', 'option_value'=>'downtonabb.ey'));
Expand Down
Loading

0 comments on commit e511b74

Please sign in to comment.