diff --git a/tests/TestOfFavoritePostMySQLDAO.php b/tests/TestOfFavoritePostMySQLDAO.php index 0a206d3291..4944076481 100644 --- a/tests/TestOfFavoritePostMySQLDAO.php +++ b/tests/TestOfFavoritePostMySQLDAO.php @@ -23,447 +23,683 @@ * @copyright 2009-2013 Amy Unruh, Gina Trapani * @author Amy Unruh */ - -require_once dirname(__FILE__).'/init.tests.php'; -require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/autorun.php'; -require_once THINKUP_WEBAPP_PATH.'config.inc.php'; -require_once THINKUP_WEBAPP_PATH.'plugins/twitter/model/class.TwitterPlugin.php'; -require_once THINKUP_WEBAPP_PATH.'plugins/facebook/model/class.FacebookPlugin.php'; - +require_once dirname ( __FILE__ ) . '/init.tests.php'; +require_once THINKUP_WEBAPP_PATH . '_lib/extlib/simpletest/autorun.php'; +require_once THINKUP_WEBAPP_PATH . 'config.inc.php'; +require_once THINKUP_WEBAPP_PATH . 'plugins/twitter/model/class.TwitterPlugin.php'; +require_once THINKUP_WEBAPP_PATH . 'plugins/facebook/model/class.FacebookPlugin.php'; class TestOfFavoritePostMySQLDAO extends ThinkUpUnitTestCase { - /** - * - * @var FavoritePostMySQLDAO - */ - protected $dao; - - public function setUp() { - parent::setUp(); - $config = Config::getInstance(); - $this->builders = self::buildData(); - $this->dao = new FavoritePostMySQLDAO(); - } - - protected function buildData() { - - $builders = array(); - $builders[] = FixtureBuilder::build('owner_instances', array('owner_id'=>1, 'instance_id'=>1)); - - $builders[] = FixtureBuilder::build('users', array('user_id'=>13, 'user_name'=>'ev', - 'full_name'=>'Ev Williams', 'avatar'=>'avatar.jpg', 'is_protected'=>0, 'follower_count'=>10, - 'last_updated'=>'2005-01-01 13:48:05', 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('users', array('user_id'=>19, 'user_name'=>'linkbaiter', - 'full_name'=>'Link Baiter', 'avatar'=>'avatar.jpg', 'is_protected'=>0, 'follower_count'=>70, - 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('users', array('user_id'=>20, 'user_name'=>'user1', - 'full_name'=>'User 1', 'avatar'=>'avatar.jpg', 'is_protected'=>0, 'follower_count'=>90, - 'network'=>'twitter')); - - //protected user - $builders[] = FixtureBuilder::build('users', array('user_id'=>21, 'user_name'=>'user2', - 'full_name'=>'User 2', 'avatar'=>'avatar.jpg', 'is_protected'=>1, 'follower_count'=>80, - 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('users', array('user_id'=>22, 'user_name'=>'quoter', - 'full_name'=>'Quotables', 'is_protected'=>0, 'follower_count'=>80, 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('users', array('user_id'=>23, 'user_name'=>'user3', - 'full_name'=>'User 3', 'is_protected'=>0, 'follower_count'=>100, 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('users', array('user_id'=>24, 'user_name'=>'notonpublictimeline', - 'full_name'=>'Not on Public Timeline', 'is_protected'=>1, 'network'=>'twitter', 'follower_count'=>100)); - - //Make public - $builders[] = FixtureBuilder::build('instances', array('network_user_id'=>13, 'network_username'=>'ev', - 'is_public'=>1, 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('instances', array('network_user_id'=>19, 'network_username'=>'linkbaiter', - 'is_public'=>1, 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('instances', array('network_user_id'=>23, 'network_username'=>'user3', - 'is_public'=>1, 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('instances', array('network_user_id'=>24, - 'network_username'=>'notonpublictimeline', 'is_public'=>0, 'network'=>'twitter')); - - //Add straight text posts from ev - $counter = 0; - while ($counter < 40) { - $pseudo_minute = str_pad($counter, 2, "0", STR_PAD_LEFT); - if ($counter % 3 == 0) { - $source = 'Tweetie for Mac'; - } else if ($counter % 3 == 1) { - $source = 'Tweet Button'; - } else { - $source = 'web'; - } - $is_protected = $counter == 18 ? 1 : 0; // post with id 18 is protected - $builders[] = FixtureBuilder::build('posts', array('post_id'=>$counter, 'author_user_id'=>13, - 'author_username'=>'ev', 'author_fullname'=>'Ev Williams', 'author_avatar'=>'avatar.jpg', - 'post_text'=>'This is post '.$counter, 'source'=>$source, 'pub_date'=>'2006-01-01 00:'. - $pseudo_minute.':00', 'reply_count_cache'=>rand(0, 4), 'retweet_count_cache'=>5, 'network'=>'twitter', - 'in_reply_to_post_id'=>null, 'in_retweet_of_post_id'=>null, 'is_geo_encoded'=>0, - 'is_protected' => $is_protected)); - $counter++; - } - - //Add link posts from 'linkbaiter' - $counter = 0; - while ($counter < 40) { - $post_id = $counter + 80; - $is_protected = $counter == 18 ? 1 : 0; // post with id 18 is protected - $pseudo_minute = str_pad(($counter), 2, "0", STR_PAD_LEFT); - $builders[] = FixtureBuilder::build('posts', array('post_id'=>$post_id, 'author_user_id'=>19, - 'author_username'=>'linkbaiter', 'author_fullname'=>'Link Baiter', 'is_geo_encoded'=>0, - 'post_text'=>'This is link post '.$counter, 'source'=>'web', 'pub_date'=>'2006-03-01 00:'. - $pseudo_minute.':00', 'reply_count_cache'=>0, 'retweet_count_cache'=>0, 'network'=>'twitter', - 'is_protected' => $is_protected)); - - $builders[] = FixtureBuilder::build('links', array('url'=>'http://example.com/'.$counter, - 'explanded_url'=>'http://example.com/'.$counter.'.html', 'title'=>'Link $counter', 'clicks'=>0, - 'post_id'=>$post_id, 'image_src'=>'')); - - $counter++; - } - - $builders[] = FixtureBuilder::build('posts', array('post_id'=>10822735852740608, 'author_user_id'=>23, - 'author_username'=>'user3', 'author_fullname'=>'User 3', 'network'=>'twitter', - 'post_text'=>'@nytimes has posted an interactive panoramic photo that shows how Times Square has changed over'. - ' the last 20 years http://nyti.ms/hmTVzP', - 'source'=>'web', 'pub_date'=>'-300s', 'reply_count_cache'=>0, 'retweet_count_cache'=>0, - 'location'=>'New York City', 'is_geo_encoded'=>0, 'is_protected' => 0)); - - // have 'user1' favorite some of ev's posts - for ($i = 0; $i < 20; $i++) { - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>$i, - 'author_user_id'=>13, 'fav_of_user_id'=>20, 'network'=>'twitter')); - } - // have 'user1' favorite some linkbaiter posts - for ($i = 80; $i < 100; $i++) { - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>$i, - 'author_user_id'=>19, 'fav_of_user_id'=>20, 'network'=>'twitter')); - } - // have 'user2' favorite one of the same linkbaiter posts - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>87, - 'author_user_id'=>19, 'fav_of_user_id'=>21, 'network'=>'twitter')); - - return $builders; - } - - public function tearDown() { - $this->builders = null; - parent::tearDown(); - } - - /** - * Test constructor both directly and via factory - */ - public function testConstructor() { - $dao = new FavoritePostMySQLDAO(); - $this->assertTrue(isset($dao)); - $dao = DAOFactory::getDAO('FavoritePostDAO'); - $this->assertTrue(isset($dao)); - } - - /** - * Test creation of fav post, where the post has not yet been saved to database. - */ - public function testAddFavoriteFullPost() { - $favoriter_id = 21; //user 2 - $vals = $this->buildPostArray1(); - $res = $this->dao->addFavorite($favoriter_id, $vals); - $this->assertEqual($res, 1); - } - - public function testAddFavoriteMissingPostData() { - $favoriter_id = 21; //user 2 - $vals = $this->buildFavoriteArray(); - $this->expectException('Exception', - 'Error: Favorited post ID 345840895515801 is not in storage and could not be inserted.'); - $res = $this->dao->addFavorite($favoriter_id, $vals); - $this->assertEqual($res, 1); - } - - /** - * Test creation of fav post, where post already exists in db, but not favorite bookkeeping, - * and so we are just adding an entry to the favorites table. - */ - public function testAddFavoritePostExists() { - $favoriter_id = 21; //user 2 - $vals = $this->buildPostArray2(); - $res = $this->dao->addFavorite($favoriter_id, $vals); - $this->assertEqual($res, 1); - // now try again-- this time the 'add' should return 0 - $res = $this->dao->addFavorite($favoriter_id, $vals); - $this->assertEqual($res, 0); - } - - /** - * Test unfavoriting of fav'd post - */ - public function testFavPostUnfav() { - $res = $this->dao->unFavorite(81, 20, 'twitter'); - $this->assertEqual($res, 1); - } - - /** - * Test attempted unfav of a post that is favorited, but not by the given user - */ - public function testNonFavPostUnfav() { - $res = $this->dao->unFavorite(82, 19, 'twitter'); - $this->assertEqual($res, 0); - } - - /** - * Check unfavoriting the same post by multiple users - */ - public function testMultipleFavsOfPost() { - $this->dao = new FavoritePostMySQLDAO(); - $res = $this->dao->unFavorite(87, 20, 'twitter'); - $this->assertEqual($res, 1); - $res = $this->dao->unFavorite(87, 21, 'twitter'); - $this->assertEqual($res, 1); - $res = $this->dao->unFavorite(87, 20, 'twitter'); - $this->assertEqual($res, 0); - } - - /** - * Test fetch of N favorited posts for a given user by userid - */ - public function testGetAllFavsForUserID() { - $this->dao = new FavoritePostMySQLDAO(); - $res = $this->dao->getAllFavoritePosts(20, 'twitter', 6, 1, false); //not public - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 6); - $this->assertEqual($res[0]->post_text, 'This is link post 19'); - $this->assertEqual($res[1]->post_text, 'This is link post 18'); - $i = 0; - while ($i < 6) { - $this->debug( $res[$i]->post_text ); - $i++; - } - // just check that we get the same result w/out the explicit arg - $res = $this->dao->getAllFavoritePosts(20, 'twitter', 6); //not public - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 6); - $this->assertEqual($res[0]->post_text, 'This is link post 19'); - $this->assertEqual($res[1]->post_text, 'This is link post 18'); - - //iterator - $res = $this->dao->getAllFavoritePostsIterator(20, 'twitter', 6); - $this->assertIsA($res, "PostIterator"); - $i = 0; - while ($i < 6) { - $this->assertTrue($res->valid()); - $seeme = $res->current(); - $this->debug( $seeme->post_text ); - $res->next(); - $i++; - } - $this->assertFalse($res->valid()); - } - - /** - * Test fetch of N favorited posts for a given user by userid, but where public = true. - * post with id 18 should not be fetched this time - */ - public function testGetAllFavsForUserID2() { - $this->dao = new FavoritePostMySQLDAO(); - $res = $this->dao->getAllFavoritePosts(20, 'twitter', 6, 1, true); // public - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 6); - $this->assertEqual($res[0]->post_text, 'This is link post 19'); - $this->assertEqual($res[1]->post_text, 'This is link post 17'); - $i = 0; - while ($i < 6) { - $this->debug( $res[$i]->post_text ); - $i++; - } - } - - /** - * Test fetch of all favorited posts for a given user by username - */ - public function testGetAllFavsForUsername() { - $dao = new FavoritePostMySQLDAO(); - $res = $this->dao->getAllFavoritePostsByUsername('user1', 'twitter', 100); - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 40); - - //iterator - $res = $this->dao->getAllFavoritePostsByUsernameIterator('user1', 'twitter', 100); - $this->assertIsA($res, "PostIterator"); - $i = 0; - while ($i < 40) { - $this->assertTrue($res->valid()); - $res->next(); - $i++; - } - $this->assertFalse($res->valid()); - } - - /** - * Test fetch of all favorited posts for a given user with post # less than a given upper bound. - */ - public function testGetAllFavsForUserUpperBound() { - $res = $this->dao->getAllFavoritePostsUpperBound(20, 'twitter', 100, 10); - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 10); - } - - /** - * Test pagination - */ - public function testFavoritesPagination() { - $res = $this->dao->getAllFavoritePosts(20, 'twitter', 10, 3); // fetch page 3 - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 10); - $this->assertEqual($res[0]->post_text, 'This is post 19'); - } - - /** - * test fetch of all posts of a given owner that have been favorited by others - */ - public function testGetAllFavoritedPosts() { - $res = $this->dao->getAllFavoritedPosts(19, 'twitter', 30); - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 20); - $this->assertEqual($res[0]->post_text, 'This is link post 7'); - $this->assertEqual($res[1]->post_text, 'This is link post 19'); - } - - /** - * test fetch of information for all users who have favorited a given post - */ - public function testGetFavdsOfPost() { - $res = $this->dao->getUsersWhoFavedPost(87); // twitter is default network - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 2); - $res = $this->dao->getUsersWhoFavedPost(87, 'twitter', true); - $this->assertIsA($res, "array"); - $this->assertEqual(count($res), 2); - } - - public function testGetFavoritesFromOneYearAgo() { - //build post published one year ago today - $builders[] = FixtureBuilder::build('posts', array('post_id'=>'abadadfd1212', 'author_user_id'=>'19', - 'author_username'=>'linkbaiter', 'author_fullname'=>'Link Baiter', 'is_geo_encoded'=>0, - 'post_text'=>'This is link post '.$counter, 'source'=>'web', 'pub_date'=>'-365d', - 'reply_count_cache'=>0, 'retweet_count_cache'=>0, 'network'=>'twitter', - 'is_protected' => 0)); - - //build favorite of that post by test user ev - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>'abadadfd1212', 'author_user_id'=>'19', - 'fav_of_user_id'=>'13', 'network'=>'twitter')); - - //get favorites from one year ago today - $result = $this->dao->getFavoritesFromOneYearAgo('13', 'twitter'); - - //assert post is returned - $this->assertEqual(sizeof($result), 1); - $this->assertEqual($result[0]->post_id, 'abadadfd1212'); - - //build post published one year and 4 days ago today - $builders[] = FixtureBuilder::build('posts', array('post_id'=>'abadadfd1213', 'author_user_id'=>'19', - 'author_username'=>'linkbaiter', 'author_fullname'=>'Link Baiter', 'is_geo_encoded'=>0, - 'post_text'=>'This is link post '.$counter, 'source'=>'web', 'pub_date'=>'-369d', - 'reply_count_cache'=>0, 'retweet_count_cache'=>0, 'network'=>'twitter', - 'is_protected' => 0)); - - //build favorite of that post by test user ev - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>'abadadfd1213', 'author_user_id'=>'19', - 'fav_of_user_id'=>'13', 'network'=>'twitter')); - - $since_date = date("Y-m-d", strtotime("-4 day")); - //get favorites from one year ago today - $result = $this->dao->getFavoritesFromOneYearAgo('13', 'twitter', $since_date); - - //assert post is returned - $this->assertEqual(sizeof($result), 1); - $this->assertEqual($result[0]->post_id, 'abadadfd1213'); - } - - public function testGetUsersWhoFavoritedMostOfYourPosts() { - //build post published 3 days ago - $builders[] = FixtureBuilder::build('posts', array('post_id'=>'abadadfd1212', 'author_user_id'=>'19', - 'author_username'=>'linkbaiter', 'author_fullname'=>'Link Baiter', 'is_geo_encoded'=>0, - 'post_text'=>'This is link post '.$counter, 'source'=>'web', 'pub_date'=>'-3d', - 'reply_count_cache'=>0, 'retweet_count_cache'=>0, 'network'=>'twitter', - 'is_protected' => 0)); - - //build post published 4 days ago - $builders[] = FixtureBuilder::build('posts', array('post_id'=>'abadadfd1213', 'author_user_id'=>'19', - 'author_username'=>'linkbaiter', 'author_fullname'=>'Link Baiter', 'is_geo_encoded'=>0, - 'post_text'=>'This is link post '.$counter, 'source'=>'web', 'pub_date'=>'-4d', - 'reply_count_cache'=>0, 'retweet_count_cache'=>0, 'network'=>'twitter', - 'is_protected' => 0)); - - //build favorite of those posts by test user ev - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>'abadadfd1212', 'author_user_id'=>'19', - 'fav_of_user_id'=>'13', 'network'=>'twitter')); - - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>'abadadfd1213', 'author_user_id'=>'19', - 'fav_of_user_id'=>'13', 'network'=>'twitter')); - - //build favorite of that post by test user user1 - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>'abadadfd1212', 'author_user_id'=>'19', - 'fav_of_user_id'=>'20', 'network'=>'twitter')); - - //build favorite of that post by test user user2 - $builders[] = FixtureBuilder::build('favorites', array('post_id'=>'abadadfd1212', 'author_user_id'=>'19', - 'fav_of_user_id'=>'21', 'network'=>'twitter')); - - $result = $this->dao->getUsersWhoFavoritedMostOfYourPosts('19', 'twitter', 7); - $this->debug(Utils::varDumpToString($result)); - $this->assertEqual(sizeof($result), 1); - $this->assertEqual($result[0]->username, 'ev'); - } - - /** - * helper method to build a post - */ - private function buildPostArray1() { - $vals = array(); - $vals['post_id']='2904'; - $vals['author_username']='quoter'; - $vals['author_fullname']="Quoter of Quotables"; - $vals['author_avatar']='avatar.jpg'; - $vals['author_user_id']= 22; - $vals['post_text']="Go confidently in the direction of your dreams! Live the life you've imagined."; - $vals['pub_date']='3/1/2010'; - $vals['source']='web'; - $vals['network']= 'twitter'; - $vals['is_protected'] = 0; - return $vals; - } - - /** - * helper method to build a post - */ - private function buildPostArray2() { - $vals = array(); - $vals['post_id']='10822735852740608'; - $vals['author_username']='user3'; - $vals['author_fullname']="User 3"; - $vals['author_avatar']='avatar.jpg'; - $vals['author_user_id']= 23; - $vals['post_text']="@nytimes has posted an interactive panoramic photo that shows how Times Square has ". - "changed over the last 20 years http://nyti.ms/hmTVzP"; - $vals['pub_date']='-200s'; - $vals['source']='web'; - $vals['network']= 'twitter'; - $vals['is_protected'] = 0; - return $vals; - } - - private function buildFavoriteArray() { - $vals = array(); - $vals["favoriter_id"]= "1075560752"; - $vals["network"] = "facebook page"; - $vals["author_user_id"] = "340319429401281"; - $vals["post_id"]="345840895515801"; - return $vals; - } + /** + * + * @var FavoritePostMySQLDAO + */ + protected $dao; + public function setUp() { + parent::setUp (); + $config = Config::getInstance (); + $this->builders = self::buildData (); + $this->dao = new FavoritePostMySQLDAO (); + } + protected function buildData() { + $builders = array (); + $builders [] = FixtureBuilder::build ( 'owner_instances', array ( + 'owner_id' => 1, + 'instance_id' => 1 + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 13, + 'user_name' => 'ev', + 'full_name' => 'Ev Williams', + 'avatar' => 'avatar.jpg', + 'is_protected' => 0, + 'follower_count' => 10, + 'last_updated' => '2005-01-01 13:48:05', + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 19, + 'user_name' => 'linkbaiter', + 'full_name' => 'Link Baiter', + 'avatar' => 'avatar.jpg', + 'is_protected' => 0, + 'follower_count' => 70, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 20, + 'user_name' => 'user1', + 'full_name' => 'User 1', + 'avatar' => 'avatar.jpg', + 'is_protected' => 0, + 'follower_count' => 90, + 'network' => 'twitter' + ) ); + + // protected user + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 21, + 'user_name' => 'user2', + 'full_name' => 'User 2', + 'avatar' => 'avatar.jpg', + 'is_protected' => 1, + 'follower_count' => 80, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 22, + 'user_name' => 'quoter', + 'full_name' => 'Quotables', + 'is_protected' => 0, + 'follower_count' => 80, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 23, + 'user_name' => 'user3', + 'full_name' => 'User 3', + 'is_protected' => 0, + 'follower_count' => 100, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 24, + 'user_name' => 'notonpublictimeline', + 'full_name' => 'Not on Public Timeline', + 'is_protected' => 1, + 'network' => 'twitter', + 'follower_count' => 100 + ) ); + + // Make public + $builders [] = FixtureBuilder::build ( 'instances', array ( + 'network_user_id' => 13, + 'network_username' => 'ev', + 'is_public' => 1, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'instances', array ( + 'network_user_id' => 19, + 'network_username' => 'linkbaiter', + 'is_public' => 1, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'instances', array ( + 'network_user_id' => 23, + 'network_username' => 'user3', + 'is_public' => 1, + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'instances', array ( + 'network_user_id' => 24, + 'network_username' => 'notonpublictimeline', + 'is_public' => 0, + 'network' => 'twitter' + ) ); + + // Add straight text posts from ev + $counter = 0; + while ( $counter < 40 ) { + $pseudo_minute = str_pad ( $counter, 2, "0", STR_PAD_LEFT ); + if ($counter % 3 == 0) { + $source = 'Tweetie for Mac'; + } else if ($counter % 3 == 1) { + $source = 'Tweet Button'; + } else { + $source = 'web'; + } + $is_protected = $counter == 18 ? 1 : 0; // post with id 18 is protected + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => $counter, + 'author_user_id' => 13, + 'author_username' => 'ev', + 'author_fullname' => 'Ev Williams', + 'author_avatar' => 'avatar.jpg', + 'post_text' => 'This is post ' . $counter, + 'source' => $source, + 'pub_date' => '2006-01-01 00:' . $pseudo_minute . ':00', + 'reply_count_cache' => rand ( 0, 4 ), + 'retweet_count_cache' => 5, + 'network' => 'twitter', + 'in_reply_to_post_id' => null, + 'in_retweet_of_post_id' => null, + 'is_geo_encoded' => 0, + 'is_protected' => $is_protected + ) ); + $counter ++; + } + + // Add link posts from 'linkbaiter' + $counter = 0; + while ( $counter < 40 ) { + $post_id = $counter + 80; + $is_protected = $counter == 18 ? 1 : 0; // post with id 18 is protected + $pseudo_minute = str_pad ( ($counter), 2, "0", STR_PAD_LEFT ); + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => $post_id, + 'author_user_id' => 19, + 'author_username' => 'linkbaiter', + 'author_fullname' => 'Link Baiter', + 'is_geo_encoded' => 0, + 'post_text' => 'This is link post ' . $counter, + 'source' => 'web', + 'pub_date' => '2006-03-01 00:' . $pseudo_minute . ':00', + 'reply_count_cache' => 0, + 'retweet_count_cache' => 0, + 'network' => 'twitter', + 'is_protected' => $is_protected + ) ); + + $builders [] = FixtureBuilder::build ( 'links', array ( + 'url' => 'http://example.com/' . $counter, + 'explanded_url' => 'http://example.com/' . $counter . '.html', + 'title' => 'Link $counter', + 'clicks' => 0, + 'post_id' => $post_id, + 'image_src' => '' + ) ); + + $counter ++; + } + + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 10822735852740608, + 'author_user_id' => 23, + 'author_username' => 'user3', + 'author_fullname' => 'User 3', + 'network' => 'twitter', + 'post_text' => '@nytimes has posted an interactive panoramic photo that shows how Times Square has changed over' . ' the last 20 years http://nyti.ms/hmTVzP', + 'source' => 'web', + 'pub_date' => '-300s', + 'reply_count_cache' => 0, + 'retweet_count_cache' => 0, + 'location' => 'New York City', + 'is_geo_encoded' => 0, + 'is_protected' => 0 + ) ); + + // have 'user1' favorite some of ev's posts + for($i = 0; $i < 20; $i ++) { + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => $i, + 'author_user_id' => 13, + 'fav_of_user_id' => 20, + 'network' => 'twitter' + ) ); + } + // have 'user1' favorite some linkbaiter posts + for($i = 80; $i < 100; $i ++) { + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => $i, + 'author_user_id' => 19, + 'fav_of_user_id' => 20, + 'network' => 'twitter' + ) ); + } + // have 'user2' favorite one of the same linkbaiter posts + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 87, + 'author_user_id' => 19, + 'fav_of_user_id' => 21, + 'network' => 'twitter' + ) ); + + return $builders; + } + public function tearDown() { + $this->builders = null; + parent::tearDown (); + } + + /** + * Test constructor both directly and via factory + */ + public function testConstructor() { + $dao = new FavoritePostMySQLDAO (); + $this->assertTrue ( isset ( $dao ) ); + $dao = DAOFactory::getDAO ( 'FavoritePostDAO' ); + $this->assertTrue ( isset ( $dao ) ); + } + + /** + * Test creation of fav post, where the post has not yet been saved to database. + */ + public function testAddFavoriteFullPost() { + $favoriter_id = 21; // user 2 + $vals = $this->buildPostArray1 (); + $res = $this->dao->addFavorite ( $favoriter_id, $vals ); + $this->assertEqual ( $res, 1 ); + } + public function testAddFavoriteMissingPostData() { + $favoriter_id = 21; // user 2 + $vals = $this->buildFavoriteArray (); + $this->expectException ( 'Exception', 'Error: Favorited post ID 345840895515801 is not in storage and could not be inserted.' ); + $res = $this->dao->addFavorite ( $favoriter_id, $vals ); + $this->assertEqual ( $res, 1 ); + } + + /** + * Test creation of fav post, where post already exists in db, but not favorite bookkeeping, + * and so we are just adding an entry to the favorites table. + */ + public function testAddFavoritePostExists() { + $favoriter_id = 21; // user 2 + $vals = $this->buildPostArray2 (); + $res = $this->dao->addFavorite ( $favoriter_id, $vals ); + $this->assertEqual ( $res, 1 ); + // now try again-- this time the 'add' should return 0 + $res = $this->dao->addFavorite ( $favoriter_id, $vals ); + $this->assertEqual ( $res, 0 ); + } + + /** + * Test unfavoriting of fav'd post + */ + public function testFavPostUnfav() { + $res = $this->dao->unFavorite ( 81, 20, 'twitter' ); + $this->assertEqual ( $res, 1 ); + } + + /** + * Test attempted unfav of a post that is favorited, but not by the given user + */ + public function testNonFavPostUnfav() { + $res = $this->dao->unFavorite ( 82, 19, 'twitter' ); + $this->assertEqual ( $res, 0 ); + } + + /** + * Check unfavoriting the same post by multiple users + */ + public function testMultipleFavsOfPost() { + $this->dao = new FavoritePostMySQLDAO (); + $res = $this->dao->unFavorite ( 87, 20, 'twitter' ); + $this->assertEqual ( $res, 1 ); + $res = $this->dao->unFavorite ( 87, 21, 'twitter' ); + $this->assertEqual ( $res, 1 ); + $res = $this->dao->unFavorite ( 87, 20, 'twitter' ); + $this->assertEqual ( $res, 0 ); + } + + /** + * Test fetch of N favorited posts for a given user by userid + */ + public function testGetAllFavsForUserID() { + $this->dao = new FavoritePostMySQLDAO (); + $res = $this->dao->getAllFavoritePosts ( 20, 'twitter', 6, 1, false ); // not public + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 6 ); + $this->assertEqual ( $res [0]->post_text, 'This is link post 19' ); + $this->assertEqual ( $res [1]->post_text, 'This is link post 18' ); + $i = 0; + while ( $i < 6 ) { + $this->debug ( $res [$i]->post_text ); + $i ++; + } + // just check that we get the same result w/out the explicit arg + $res = $this->dao->getAllFavoritePosts ( 20, 'twitter', 6 ); // not public + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 6 ); + $this->assertEqual ( $res [0]->post_text, 'This is link post 19' ); + $this->assertEqual ( $res [1]->post_text, 'This is link post 18' ); + + // iterator + $res = $this->dao->getAllFavoritePostsIterator ( 20, 'twitter', 6 ); + $this->assertIsA ( $res, "PostIterator" ); + $i = 0; + while ( $i < 6 ) { + $this->assertTrue ( $res->valid () ); + $seeme = $res->current (); + $this->debug ( $seeme->post_text ); + $res->next (); + $i ++; + } + $this->assertFalse ( $res->valid () ); + } + + /** + * Test fetch of N favorited posts for a given user by userid, but where public = true. + * post with id 18 should not be fetched this time + */ + public function testGetAllFavsForUserID2() { + $this->dao = new FavoritePostMySQLDAO (); + $res = $this->dao->getAllFavoritePosts ( 20, 'twitter', 6, 1, true ); // public + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 6 ); + $this->assertEqual ( $res [0]->post_text, 'This is link post 19' ); + $this->assertEqual ( $res [1]->post_text, 'This is link post 17' ); + $i = 0; + while ( $i < 6 ) { + $this->debug ( $res [$i]->post_text ); + $i ++; + } + } + + /** + * Test fetch of all favorited posts for a given user by username + */ + public function testGetAllFavsForUsername() { + $dao = new FavoritePostMySQLDAO (); + $res = $this->dao->getAllFavoritePostsByUsername ( 'user1', 'twitter', 100 ); + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 40 ); + + // iterator + $res = $this->dao->getAllFavoritePostsByUsernameIterator ( 'user1', 'twitter', 100 ); + $this->assertIsA ( $res, "PostIterator" ); + $i = 0; + while ( $i < 40 ) { + $this->assertTrue ( $res->valid () ); + $res->next (); + $i ++; + } + $this->assertFalse ( $res->valid () ); + } + + /** + * Test fetch of all favorited posts for a given user with post # less than a given upper bound. + */ + public function testGetAllFavsForUserUpperBound() { + $res = $this->dao->getAllFavoritePostsUpperBound ( 20, 'twitter', 100, 10 ); + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 10 ); + } + + /** + * Test pagination + */ + public function testFavoritesPagination() { + $res = $this->dao->getAllFavoritePosts ( 20, 'twitter', 10, 3 ); // fetch page 3 + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 10 ); + $this->assertEqual ( $res [0]->post_text, 'This is post 19' ); + } + + /** + * test fetch of all posts of a given owner that have been favorited by others + */ + public function testGetAllFavoritedPosts() { + $res = $this->dao->getAllFavoritedPosts ( 19, 'twitter', 30 ); + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 20 ); + $this->assertEqual ( $res [0]->post_text, 'This is link post 7' ); + $this->assertEqual ( $res [1]->post_text, 'This is link post 19' ); + } + + /** + * test fetch of information for all users who have favorited a given post + */ + public function testGetFavdsOfPost() { + $res = $this->dao->getUsersWhoFavedPost ( 87 ); // twitter is default network + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 2 ); + $res = $this->dao->getUsersWhoFavedPost ( 87, 'twitter', true ); + $this->assertIsA ( $res, "array" ); + $this->assertEqual ( count ( $res ), 2 ); + } + public function testGetFavoritesFromOneYearAgo() { + // build post published one year ago today + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abadadfd1212', + 'author_user_id' => '19', + 'author_username' => 'linkbaiter', + 'author_fullname' => 'Link Baiter', + 'is_geo_encoded' => 0, + 'post_text' => 'This is link post ' . $counter, + 'source' => 'web', + 'pub_date' => '-365d', + 'reply_count_cache' => 0, + 'retweet_count_cache' => 0, + 'network' => 'twitter', + 'is_protected' => 0 + ) ); + + // build favorite of that post by test user ev + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 'abadadfd1212', + 'author_user_id' => '19', + 'fav_of_user_id' => '13', + 'network' => 'twitter' + ) ); + + // get favorites from one year ago today + $result = $this->dao->getFavoritesFromOneYearAgo ( '13', 'twitter' ); + + // assert post is returned + $this->assertEqual ( sizeof ( $result ), 1 ); + $this->assertEqual ( $result [0]->post_id, 'abadadfd1212' ); + + // build post published one year and 4 days ago today + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abadadfd1213', + 'author_user_id' => '19', + 'author_username' => 'linkbaiter', + 'author_fullname' => 'Link Baiter', + 'is_geo_encoded' => 0, + 'post_text' => 'This is link post ' . $counter, + 'source' => 'web', + 'pub_date' => '-369d', + 'reply_count_cache' => 0, + 'retweet_count_cache' => 0, + 'network' => 'twitter', + 'is_protected' => 0 + ) ); + + // build favorite of that post by test user ev + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 'abadadfd1213', + 'author_user_id' => '19', + 'fav_of_user_id' => '13', + 'network' => 'twitter' + ) ); + + $since_date = date ( "Y-m-d", strtotime ( "-4 day" ) ); + // get favorites from one year ago today + $result = $this->dao->getFavoritesFromOneYearAgo ( '13', 'twitter', $since_date ); + + // assert post is returned + $this->assertEqual ( sizeof ( $result ), 1 ); + $this->assertEqual ( $result [0]->post_id, 'abadadfd1213' ); + } + public function testGetUsersWhoFavoritedMostOfYourPosts() { + // build post published 3 days ago + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abadadfd1212', + 'author_user_id' => '19', + 'author_username' => 'linkbaiter', + 'author_fullname' => 'Link Baiter', + 'is_geo_encoded' => 0, + 'post_text' => 'This is link post ' . $counter, + 'source' => 'web', + 'pub_date' => '-3d', + 'reply_count_cache' => 0, + 'retweet_count_cache' => 0, + 'network' => 'twitter', + 'is_protected' => 0 + ) ); + + // build post published 4 days ago + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abadadfd1213', + 'author_user_id' => '19', + 'author_username' => 'linkbaiter', + 'author_fullname' => 'Link Baiter', + 'is_geo_encoded' => 0, + 'post_text' => 'This is link post ' . $counter, + 'source' => 'web', + 'pub_date' => '-4d', + 'reply_count_cache' => 0, + 'retweet_count_cache' => 0, + 'network' => 'twitter', + 'is_protected' => 0 + ) ); + + // build favorite of those posts by test user ev + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 'abadadfd1212', + 'author_user_id' => '19', + 'fav_of_user_id' => '13', + 'network' => 'twitter' + ) ); + + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 'abadadfd1213', + 'author_user_id' => '19', + 'fav_of_user_id' => '13', + 'network' => 'twitter' + ) ); + + // build favorite of that post by test user user1 + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 'abadadfd1212', + 'author_user_id' => '19', + 'fav_of_user_id' => '20', + 'network' => 'twitter' + ) ); + + // build favorite of that post by test user user2 + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 'abadadfd1212', + 'author_user_id' => '19', + 'fav_of_user_id' => '21', + 'network' => 'twitter' + ) ); + + $result = $this->dao->getUsersWhoFavoritedMostOfYourPosts ( '19', 'twitter', 7 ); + $this->debug ( Utils::varDumpToString ( $result ) ); + $this->assertEqual ( sizeof ( $result ), 1 ); + $this->assertEqual ( $result [0]->username, 'ev' ); + } + public function testGetGenderOfFavoriters() { + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 111, + 'fav_of_user_id' => 133, + 'network' => 'facebook' + ) ); + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 111, + 'fav_of_user_id' => 193, + 'network' => 'facebook' + ) ); + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 111, + 'fav_of_user_id' => 203, + 'network' => 'facebook' + ) ); + $builders [] = FixtureBuilder::build ( 'favorites', array ( + 'post_id' => 222, + 'fav_of_user_id' => 203, + 'network' => 'facebook' + ) ); + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 133, + 'gender'=>'male' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 193, + 'gender'=>'male' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 203, + 'gender'=>'female' + ) ); + $result = $this->dao->getGenderOfFavoriters ( '111'); + $this->debug ( Utils::varDumpToString ( $result ) ); + $this->assertEqual ( $result ['female_likes_count'] , '1' ); + $this->assertEqual ( $result ['male_likes_count'], '2' ); + } + + public function testGetGenderOfCommenters() { + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abadadfd12123', + 'author_user_id' => '203', + 'in_reply_to_post_id' => '111' + ) ); + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abadad', + 'author_user_id' => '193', + 'in_reply_to_post_id' => '111' + ) ); + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'd12123', + 'author_user_id' => '133', + 'in_reply_to_post_id' => '111' + ) ); + $builders [] = FixtureBuilder::build ( 'posts', array ( + 'post_id' => 'abawertyfd12123', + 'author_user_id' => '203', + 'in_reply_to_post_id' => '222' + ) ); + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 133, + 'gender'=>'male' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 193, + 'gender'=>'male' + ) ); + + $builders [] = FixtureBuilder::build ( 'users', array ( + 'user_id' => 203, + 'gender'=>'female' + ) ); + $result = $this->dao->getGenderOfCommenters ( '111' ); + $this->debug ( Utils::varDumpToString ( $result ) ); + $this->assertEqual ( $result ['female_comm_count'] , '1' ); + $this->assertEqual ( $result ['male_comm_count'], '2' ); + } + /** + * helper method to build a post + */ + private function buildPostArray1() { + $vals = array (); + $vals ['post_id'] = '2904'; + $vals ['author_username'] = 'quoter'; + $vals ['author_fullname'] = "Quoter of Quotables"; + $vals ['author_avatar'] = 'avatar.jpg'; + $vals ['author_user_id'] = 22; + $vals ['post_text'] = "Go confidently in the direction of your dreams! Live the life you've imagined."; + $vals ['pub_date'] = '3/1/2010'; + $vals ['source'] = 'web'; + $vals ['network'] = 'twitter'; + $vals ['is_protected'] = 0; + return $vals; + } + + /** + * helper method to build a post + */ + private function buildPostArray2() { + $vals = array (); + $vals ['post_id'] = '10822735852740608'; + $vals ['author_username'] = 'user3'; + $vals ['author_fullname'] = "User 3"; + $vals ['author_avatar'] = 'avatar.jpg'; + $vals ['author_user_id'] = 23; + $vals ['post_text'] = "@nytimes has posted an interactive panoramic photo that shows how Times Square has " . "changed over the last 20 years http://nyti.ms/hmTVzP"; + $vals ['pub_date'] = '-200s'; + $vals ['source'] = 'web'; + $vals ['network'] = 'twitter'; + $vals ['is_protected'] = 0; + return $vals; + } + private function buildFavoriteArray() { + $vals = array (); + $vals ["favoriter_id"] = "1075560752"; + $vals ["network"] = "facebook page"; + $vals ["author_user_id"] = "340319429401281"; + $vals ["post_id"] = "345840895515801"; + return $vals; + } } diff --git a/tests/TestOfPostMySQLDAO.php b/tests/TestOfPostMySQLDAO.php index 3c15d50971..f45d14ec3f 100644 --- a/tests/TestOfPostMySQLDAO.php +++ b/tests/TestOfPostMySQLDAO.php @@ -4064,6 +4064,42 @@ public function testCountAllPostsByUserSinceDaysAgo() { $this->assertEqual($result, 32); } + + public function testGetMostFavCommentPostsByUserId() { + echo "start test\n"; + $builders = array(); + $user_id=7654321; + $now = date('Y-m-d H:i:s'); + $yesterday = date('Y-m-d H:i:s', strtotime('yesterday')); + $builders[] = FixtureBuilder::build('posts', array('id'=> 331, 'post_id'=> 1331, 'author_user_id'=>$user_id, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=> 1,'favlike_count_cache' => 6)); + + $builders[] = FixtureBuilder::build('posts', array('id'=> 341, 'post_id'=> 1341, 'author_user_id'=>$user_id, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=> 0,'favlike_count_cache' => 5)); + + $builders[] = FixtureBuilder::build('posts', array('id'=> 351, 'post_id'=> 1351, 'author_user_id'=>$user_id, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple cooment.', + 'pub_date'=>$yesterday, 'reply_count_cache'=> 0,'favlike_count_cache' => 4)); + + $builders[] = FixtureBuilder::build('posts', array('id'=> 361, 'post_id'=> 1361, 'author_user_id'=>$user_id, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=> 0,'favlike_count_cache' => 0)); + + $post_dao = new PostMySQLDAO(); + $posts = $post_dao->getMostFavCommentPostsByUserId($user_id, 'facebook'); + $this-> assertNotNull($posts); + foreach($posts as $post) { + $this->assertTrue($post instanceof Post); + $this->assertEqual($post->post_id, '1331'); + } + + } public function testSearchPostsByUsername() { $post_dao = new PostMySQLDAO(); diff --git a/tests/TestOfUserMySQLDAO.php b/tests/TestOfUserMySQLDAO.php index d1690045b2..5d72e4a327 100644 --- a/tests/TestOfUserMySQLDAO.php +++ b/tests/TestOfUserMySQLDAO.php @@ -43,14 +43,14 @@ public function setUp() { protected function buildData() { //Insert test data into test table $builders[] = FixtureBuilder::build('users', array('user_id'=>12, 'user_name'=>'jack', - 'full_name'=>'Jack Dorsey', 'avatar'=>'avatar.jpg', 'location'=>'San Francisco', + 'full_name'=>'Jack Dorsey', 'avatar'=>'avatar.jpg', 'gender'=>'', 'birthday'=>'', 'location'=>'San Francisco', 'is_verified'=>1, 'network'=>'twitter')); //Insert test data into test table $builders[] = FixtureBuilder::build('users', array('user_id'=>13, 'user_name'=>'zuck', - 'full_name'=>'Mark Zuckerberg', 'avatar'=>'avatar.jpg', 'location'=>'San Francisco', - 'network'=>'facebook')); - + 'full_name'=>'Mark Zuckerberg', 'avatar'=>'avatar.jpg', 'gender'=>'Male', 'birthday'=>'06/11/1992', + 'location'=>'San Francisco', 'network'=>'facebook')); + $this->logger = Logger::getInstance(); return $builders; } @@ -102,11 +102,15 @@ public function testGetDetailsUserExists() { $this->assertEqual($user->id, 1); $this->assertEqual($user->user_id, 12); $this->assertEqual($user->username, 'jack'); + $this->assertEqual($user->gender, ''); + $this->assertEqual($user->birthday, ''); $this->assertEqual($user->network, 'twitter'); $user = $user_dao->getDetails(13, 'facebook'); $this->assertEqual($user->id, 2); $this->assertEqual($user->user_id, 13); $this->assertEqual($user->username, 'zuck'); + $this->assertEqual($user->gender, 'Male'); + $this->assertEqual($user->birthday, '06/11/1992'); $this->assertEqual($user->network, 'facebook'); $user = $user_dao->getDetails(13, 'twitter'); @@ -129,7 +133,7 @@ public function testUpdateUser() { $user_dao = DAOFactory::getDAO('UserDAO'); $user_array = array('user_id'=>'13', 'user_name'=>'ginatrapani', 'full_name'=>'Gina Trapani', - 'avatar'=>'avatar.jpg', 'location'=>'NYC', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', + 'avatar'=>'avatar.jpg', 'gender'=>'', 'birthday'=>'', 'location'=>'NYC', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', 'is_verified'=>1, 'is_protected'=>0, 'follower_count'=>5000, 'post_count'=>1000, 'joined'=>'2007-03-06 13:48:05', 'network'=>'twitter', 'last_post_id'=>'abc102'); $user = new User($user_array, 'Test Insert'); @@ -138,11 +142,13 @@ public function testUpdateUser() { $this->assertEqual($user_from_db->user_id, '13'); $this->assertEqual($user_from_db->username, 'ginatrapani'); $this->assertEqual($user_from_db->avatar, 'avatar.jpg'); + $this->assertEqual($user_from_db->gender, ''); + $this->assertEqual($user_from_db->birthday, ''); $this->assertEqual($user_from_db->location, 'NYC'); $this->assertTrue($user_from_db->is_verified); $user_array = array('user_id'=>13, 'user_name'=>'ginatrapanichanged', 'full_name'=>'Gina Trapani ', - 'avatar'=>'avatara.jpg', 'location'=>'San Diego', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', + 'avatar'=>'avatara.jpg', 'gender'=>'', 'birthday'=>'', 'location'=>'San Diego', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', 'is_verified'=>0, 'is_protected'=>0, 'follower_count'=>5000, 'post_count'=>1000, 'joined'=>'2007-03-06 13:48:05', 'network'=>'twitter'); $user1 = new User($user_array, 'Test Update'); @@ -151,12 +157,14 @@ public function testUpdateUser() { $this->assertEqual($user_from_db->user_id, 13); $this->assertEqual($user_from_db->username, 'ginatrapanichanged'); $this->assertEqual($user_from_db->avatar, 'avatara.jpg'); + $this->assertEqual($user_from_db->gender, ''); + $this->assertEqual($user_from_db->birthday, ''); $this->assertEqual($user_from_db->location, 'San Diego'); $this->assertFalse($user_from_db->is_verified); //Test no username set $user_array = array('user_id'=>13, 'user_name'=>null, 'full_name'=>'Gina Trapani ', - 'avatar'=>'avatara.jpg', 'location'=>'San Diego', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', + 'avatar'=>'avatara.jpg', 'gender'=>'', 'birthday'=>'', 'location'=>'San Diego', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', 'is_verified'=>1, 'is_protected'=>0, 'follower_count'=>5000, 'post_count'=>1000, 'joined'=>'2007-03-06 13:48:05', 'network'=>'twitter'); $user1 = new User($user_array, 'Test Update'); @@ -170,12 +178,12 @@ public function testUpdateUsers() { $user_dao = DAOFactory::getDAO('UserDAO'); $user_array1 = array('id'=>2, 'user_id'=>'13', 'user_name'=>'ginatrapani', 'full_name'=>'Gina Trapani', - 'avatar'=>'avatar.jpg', 'location'=>'NYC', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', + 'avatar'=>'avatar.jpg', 'gender'=>'', 'birthday'=>'', 'location'=>'NYC', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', 'is_verified'=>1, 'is_protected'=>0, 'follower_count'=>5000, 'post_count'=>1000, 'joined'=>'2007-03-06 13:48:05', 'network'=>'twitter', 'last_post_id'=>'abc123'); $user1 = new User($user_array1, 'Test'); $user_array2 = array('id'=>3, 'user_id'=>'14', 'user_name'=>'anildash', 'full_name'=>'Anil Dash', - 'avatar'=>'avatar.jpg', 'location'=>'NYC', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', + 'avatar'=>'avatar.jpg', 'gender'=>'', 'birthday'=>'', 'location'=>'NYC', 'description'=>'Blogger', 'url'=>'http://ginatrapani.org', 'is_verified'=>1, 'is_protected'=>0, 'follower_count'=>5000, 'post_count'=>1000, 'joined'=>'2007-03-06 13:48:05', 'network'=>'twitter', 'last_post_id'=>'abc456'); $user2 = new User($user_array2, 'Test'); @@ -196,6 +204,8 @@ public function testGetUserByNameUserExists() { $this->assertEqual($user->user_id, 12); $this->assertEqual($user->username, 'jack'); $this->assertEqual($user->full_name, 'Jack Dorsey'); + $this->assertEqual($user->gender, ''); + $this->assertEqual($user->birthday, ''); $this->assertEqual($user->location, 'San Francisco'); } @@ -256,6 +266,8 @@ private function buildSearchData() { 'author_user_id' => '100', 'author_username' => 'ecucurella', 'author_fullname' => 'Eduard Cucurella', + 'author_gender' => '', + 'author_birthday' => '', 'author_avatar' => 'http://aa.com', 'author_follower_count' => 0, 'post_text' => '#Messi is the best http://flic.kr/p/ http://flic.kr/a/', @@ -286,6 +298,8 @@ private function buildSearchData() { 'author_user_id' => '101', 'author_username' => 'vetcastellnou', 'author_fullname' => 'Veterans Castellnou', + 'author_gender' => '', + 'author_birthday' => '', 'author_avatar' => 'http://aa.com', 'author_follower_count' => 0, 'post_text' => 'Post without any hashtag http://flic.kr/p/', @@ -316,6 +330,8 @@ private function buildSearchData() { 'author_user_id' => '102', 'author_username' => 'efectivament', 'author_fullname' => 'efectivament', + 'author_gender' => '', + 'author_birthday' => '', 'author_avatar' => 'http://aa.com', 'author_follower_count' => 0, 'post_text' => 'Post with #Messi hashtag http://flic.kr/p/', @@ -346,6 +362,8 @@ private function buildSearchData() { 'author_user_id' => '102', 'author_username' => 'efectivament', 'author_fullname' => 'efectivament', + 'author_gender' => '', + 'author_birthday' => '', 'author_avatar' => 'http://aa.com', 'author_follower_count' => 0, 'post_text' => 'Post without any hashtag 2', diff --git a/webapp/_lib/class.Mailer.php b/webapp/_lib/class.Mailer.php index a81c299d94..36d326f1ef 100644 --- a/webapp/_lib/class.Mailer.php +++ b/webapp/_lib/class.Mailer.php @@ -170,9 +170,10 @@ public static function mailViaMandrill($to, $subject, $message) { if (Utils::isTest()) { self::setLastMail(json_encode($message)); } else { + $result = $mandrill->messages->send($message, $async, $ip_pool); //DEBUG - //print_r($result); + print_r($result); } } catch (Mandrill_Error $e) { throw new Exception('An error occurred while sending email via Mandrill. ' . get_class($e) . diff --git a/webapp/_lib/dao/class.FavoritePostMySQLDAO.php b/webapp/_lib/dao/class.FavoritePostMySQLDAO.php index 29999b7920..5ed081df92 100755 --- a/webapp/_lib/dao/class.FavoritePostMySQLDAO.php +++ b/webapp/_lib/dao/class.FavoritePostMySQLDAO.php @@ -33,373 +33,536 @@ * @author Amy Unruh * */ -class FavoritePostMySQLDAO extends PostMySQLDAO implements FavoritePostDAO { - - public function addFavorite($favoriter_id, array $vals, $entities = null, $user_array = null) { - if (!$favoriter_id) { - throw new Exception("Error: favoriter/author user ID not set"); - } - // first add the post (if need be-- this post may have already been inserted). - $post = $this->getPost($vals['post_id'], $vals['network']); - if (!$post) { - $added_post = $this->addPostAndAssociatedInfo($vals, $entities, $user_array); - if (!$added_post) { - throw new Exception("Error: favorited post ID ". $vals['post_id'] . - " is not in storage and could not be inserted."); - } - } - - $q = "INSERT IGNORE INTO #prefix#favorites (post_id, author_user_id, fav_of_user_id, network) "; - $q .= "VALUES ( :post_id, :user_id, :fav_of_user_id, :network) "; - $vars = array( - ':post_id' => (string) $vals['post_id'], - ':user_id' => (string) $vals['author_user_id'], - ':fav_of_user_id' => (string) $favoriter_id, - ':network' => $vals['network'] - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $res = $this->execute($q, $vars); - return $this->getUpdateCount($res); - } - - public function unFavorite($post_id, $user_id, $network = 'twitter') { - $q = "DELETE FROM #prefix#favorites WHERE post_id = :post_id "; - $q .= "AND fav_of_user_id = :user_id AND network = :network"; - $vars = array( - ':post_id' => (string) $post_id, - ':user_id' => (string) $user_id, - ':network' => $network, - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $res = $this->execute($q, $vars); - return $this->getUpdateCount($res); - } - public function getAllFavoritePosts($owner_id, $network, $count, $page=1, $is_public = false) { - return $this->getAllFavoritePostsByUserID($owner_id, $network, $count, "pub_date", "DESC", null, - $page, false, $is_public); - } - public function getAllFavoritePostsUpperBound($owner_id, $network, $count, $ub) { - return $this->getAllFavoritePostsByUserID($owner_id, $network, $count, "pub_date", "DESC", $ub); - } - public function getAllFavoritePostsByUsername($username, $network, $count) { - return $this->getAllFavoritePostsByUsernameOrderedBy($username, $network, $count, "pub_date"); - } - - /** - * Get all favorited posts by a given user id, with configurable order by field and direction. - * Returns either an iterator or an array, as specified by $iterator. Supports pagination. - * @param int $owner_id - * @param str $network - * @param int $count - * @param str $order_by field name - * @param str $direction either "DESC" or "ASC - * @param int $ubound - * @param int $page - * @param bool $iterator - * @return array Posts with link object set or PostIterator - */ - private function getAllFavoritePostsByUserID($owner_id, $network, $count, $order_by="pub_date", $direction="DESC", - $ubound = null, $page=1, $iterator = false, $is_public = false) { - $direction = $direction=="DESC" ? "DESC": "ASC"; - $start_on_record = ($page - 1) * $count; - if ( !in_array($order_by, $this->REQUIRED_FIELDS) && !in_array($order_by, $this->OPTIONAL_FIELDS )) { - $order_by="pub_date"; - } - if ($is_public) { - $protected = ' AND p.is_protected = 0 '; - } else { - $protected = ''; - } - $q = "SELECT p.*, pub_date - interval #gmt_offset# hour AS adj_pub_date +class FavoritePostMySQLDAO extends PostMySQLDAO implements FavoritePostDAO { + public function addFavorite($favoriter_id, array $vals, $entities = null, $user_array = null) { + if (! $favoriter_id) { + throw new Exception ( "Error: favoriter/author user ID not set" ); + } + // first add the post (if need be-- this post may have already been inserted). + $post = $this->getPost ( $vals ['post_id'], $vals ['network'] ); + if (! $post) { + $added_post = $this->addPostAndAssociatedInfo ( $vals, $entities, $user_array ); + if (! $added_post) { + throw new Exception ( "Error: favorited post ID " . $vals ['post_id'] . " is not in storage and could not be inserted." ); + } + } + + $q = "INSERT IGNORE INTO #prefix#favorites (post_id, author_user_id, fav_of_user_id, network) "; + $q .= "VALUES ( :post_id, :user_id, :fav_of_user_id, :network) "; + $vars = array ( + ':post_id' => ( string ) $vals ['post_id'], + ':user_id' => ( string ) $vals ['author_user_id'], + ':fav_of_user_id' => ( string ) $favoriter_id, + ':network' => $vals ['network'] + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $res = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $res ); + } + public function unFavorite($post_id, $user_id, $network = 'twitter') { + $q = "DELETE FROM #prefix#favorites WHERE post_id = :post_id "; + $q .= "AND fav_of_user_id = :user_id AND network = :network"; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':user_id' => ( string ) $user_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $res = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $res ); + } + public function getAllFavoritePosts($owner_id, $network, $count, $page = 1, $is_public = false) { + return $this->getAllFavoritePostsByUserID ( $owner_id, $network, $count, "pub_date", "DESC", null, $page, false, $is_public ); + } + public function getAllFavoritePostsUpperBound($owner_id, $network, $count, $ub) { + return $this->getAllFavoritePostsByUserID ( $owner_id, $network, $count, "pub_date", "DESC", $ub ); + } + public function getAllFavoritePostsByUsername($username, $network, $count) { + return $this->getAllFavoritePostsByUsernameOrderedBy ( $username, $network, $count, "pub_date" ); + } + + /** + * Get all favorited posts by a given user id, with configurable order by field and direction. + * Returns either an iterator or an array, as specified by $iterator. Supports pagination. + * + * @param int $owner_id + * @param str $network + * @param int $count + * @param str $order_by + * field name + * @param str $direction + * either "DESC" or "ASC + * @param int $ubound + * @param int $page + * @param bool $iterator + * @return array Posts with link object set or PostIterator + */ + private function getAllFavoritePostsByUserID($owner_id, $network, $count, $order_by = "pub_date", $direction = "DESC", $ubound = null, $page = 1, $iterator = false, $is_public = false) { + $direction = $direction == "DESC" ? "DESC" : "ASC"; + $start_on_record = ($page - 1) * $count; + if (! in_array ( $order_by, $this->REQUIRED_FIELDS ) && ! in_array ( $order_by, $this->OPTIONAL_FIELDS )) { + $order_by = "pub_date"; + } + if ($is_public) { + $protected = ' AND p.is_protected = 0 '; + } else { + $protected = ''; + } + $q = "SELECT p.*, pub_date - interval #gmt_offset# hour AS adj_pub_date FROM (#prefix#posts p INNER JOIN #prefix#favorites f on f.post_id = p.post_id) WHERE f.fav_of_user_id = :owner_id AND p.network=:network "; - $q .= $protected; - if ($order_by == 'reply_count_cache') { - $q .= "AND reply_count_cache > 0 "; - } - if ($order_by == 'retweet_count_cache') { - $q .= "AND retweet_count_cache > 0 "; - } - if ($ubound > 0) { - $q .= "AND p.post_id < :ubound "; - } - $q .= " ORDER BY ".$order_by." ".$direction." "; - if ($count > 0) { - $q .= "LIMIT :start_on_record, :limit"; - } - $vars = array( - ':owner_id'=>$owner_id, - ':network'=>$network, - ':limit'=>$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($ubound > 0) { - $vars[':ubound'] = $ubound; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - /** - * Get favorited posts by the given username. - * Returns either an iterator or an array, as specified by $iterator. - * @param str $author_username - * @param str $network - * @param int $count - * @param str $order_by - * @param int $in_last_x_days - * @param bool $iterator - * @return array Posts with link object set or PostIterator - */ - private function getAllFavoritePostsByUsernameOrderedBy($author_username, $network="twitter", $count=0, - $order_by="pub_date", $in_last_x_days = 0, $iterator = false) { - if ( !in_array($order_by, $this->REQUIRED_FIELDS) && !in_array($order_by, $this->OPTIONAL_FIELDS )) { - $order_by="pub_date"; - } - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network - ); - $q = "SELECT p.*, pub_date - interval #gmt_offset# hour as adj_pub_date FROM + $q .= $protected; + if ($order_by == 'reply_count_cache') { + $q .= "AND reply_count_cache > 0 "; + } + if ($order_by == 'retweet_count_cache') { + $q .= "AND retweet_count_cache > 0 "; + } + if ($ubound > 0) { + $q .= "AND p.post_id < :ubound "; + } + $q .= " ORDER BY " . $order_by . " " . $direction . " "; + if ($count > 0) { + $q .= "LIMIT :start_on_record, :limit"; + } + $vars = array ( + ':owner_id' => $owner_id, + ':network' => $network, + ':limit' => $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($ubound > 0) { + $vars [':ubound'] = $ubound; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + + /** + * Get favorited posts by the given username. + * Returns either an iterator or an array, as specified by $iterator. + * + * @param str $author_username + * @param str $network + * @param int $count + * @param str $order_by + * @param int $in_last_x_days + * @param bool $iterator + * @return array Posts with link object set or PostIterator + */ + private function getAllFavoritePostsByUsernameOrderedBy($author_username, $network = "twitter", $count = 0, $order_by = "pub_date", $in_last_x_days = 0, $iterator = false) { + if (! in_array ( $order_by, $this->REQUIRED_FIELDS ) && ! in_array ( $order_by, $this->OPTIONAL_FIELDS )) { + $order_by = "pub_date"; + } + $vars = array ( + ':author_username' => $author_username, + ':network' => $network + ); + $q = "SELECT p.*, pub_date - interval #gmt_offset# hour as adj_pub_date FROM (#prefix#posts p INNER JOIN #prefix#favorites f on f.post_id = p.post_id) LEFT JOIN #prefix#users u on u.user_id = f.fav_of_user_id WHERE u.user_name = :author_username AND p.network=:network "; - - if ($in_last_x_days > 0) { - $q .= "AND pub_date >= DATE_SUB(CURDATE(), INTERVAL :in_last_x_days DAY) "; - $vars[':in_last_x_days'] = (int)$in_last_x_days; - } - if ($order_by == 'reply_count_cache') { - $q .= "AND reply_count_cache > 0 "; - } - if ($order_by == 'retweet_count_cache') { - $q .= "AND retweet_count_cache > 0 "; - } - $q .= " ORDER BY ".$order_by." DESC "; - if ($count) { - $q .= " LIMIT :limit"; - $vars[':limit'] = (int)$count; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllFavoritePostsByUsernameIterator($username, $network, $count = 0) { - return $this->getAllFavoritePostsByUsernameOrderedBy($username, $network, $count, null, null, true); - } - - public function getAllFavoritePostsIterator($user_id, $network, $count) { - return $this->getAllFavoritePostsByUserID($user_id, $network, $count, "pub_date", "DESC", null, 1, true); - } - - public function getAllFavoritedPosts($author_user_id, $network, $count, $page=1) { - return $this->getAllFavoritedPostsForUserID($author_user_id, $network, $count, $order_by="pub_date", $page); - } - - /** - * Get all the favorited posts of a user. - * @TODO Use $order_by parameter to customize sort order. - * @param int $author_user_id - * @param str $network - * @param int $count - * @param str $order_by - * @param int $page - * @param bool $iterator Whether or not to return an iterator - * @returns array Post objects - */ - private function getAllFavoritedPostsForUserID($author_user_id, $network, $count, $order_by="pub_date", - $page=1, $iterator = false) { - // $direction = $direction=="DESC" ? "DESC": "ASC"; - $start_on_record = ($page - 1) * $count; - // order-by information currently hardwired; this will probably change - // if ( !in_array($order_by, $this->REQUIRED_FIELDS) && !in_array($orderaa_by, $this->OPTIONAL_FIELDS )) { - // $order_by="pub_date"; - // } - $q = "SELECT p.*, pub_date - interval #gmt_offset# hour AS adj_pub_date, "; - //TODO: Store favlike_count_cache during Twitter crawl so we don't do this dynamic GROUP BY fakeout - $q .= "count(*) AS favlike_count_cache "; - $q .= "FROM (#prefix#posts p INNER JOIN #prefix#favorites f on f.post_id = p.post_id) "; - $q .= "WHERE p.author_user_id = :author_user_id AND p.network = :network "; - $q .= "GROUP BY p.post_text ORDER BY YEARWEEK(p.pub_date) DESC, favlike_count_cache DESC, p.pub_date DESC "; - $q .= "LIMIT :start_on_record, :limit"; - $vars = array( - ':author_user_id'=>(string) $author_user_id, - ':network'=>$network, - ':limit'=>$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getUsersWhoFavedPost($post_id, $network='twitter', $is_public = false) { - $q = "SELECT u.* FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#favorites as f on f.post_id = p.post_id "; - $q .= "INNER JOIN #prefix#users u on f.fav_of_user_id = u.user_id "; - $q .= "WHERE p.network=:network AND f.post_id=:post_id "; - if ($is_public) { - $q .= "AND p.is_protected = 0 "; - } - // could potentially order by follower count instead - $q .= " ORDER BY fav_timestamp desc "; - $vars = array( - ':post_id'=>(string) $post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - return $all_rows; - } - - public function getFavoritesFromOneYearAgo($fav_of_user_id, $network, $from_date=null) { - $q = "SELECT p.*, pub_date - interval #gmt_offset# hour AS adj_pub_date "; - $q .= "FROM #prefix#posts p INNER JOIN #prefix#favorites f on f.post_id = p.post_id + + if ($in_last_x_days > 0) { + $q .= "AND pub_date >= DATE_SUB(CURDATE(), INTERVAL :in_last_x_days DAY) "; + $vars [':in_last_x_days'] = ( int ) $in_last_x_days; + } + if ($order_by == 'reply_count_cache') { + $q .= "AND reply_count_cache > 0 "; + } + if ($order_by == 'retweet_count_cache') { + $q .= "AND retweet_count_cache > 0 "; + } + $q .= " ORDER BY " . $order_by . " DESC "; + if ($count) { + $q .= " LIMIT :limit"; + $vars [':limit'] = ( int ) $count; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllFavoritePostsByUsernameIterator($username, $network, $count = 0) { + return $this->getAllFavoritePostsByUsernameOrderedBy ( $username, $network, $count, null, null, true ); + } + public function getAllFavoritePostsIterator($user_id, $network, $count) { + return $this->getAllFavoritePostsByUserID ( $user_id, $network, $count, "pub_date", "DESC", null, 1, true ); + } + public function getAllFavoritedPosts($author_user_id, $network, $count, $page = 1) { + return $this->getAllFavoritedPostsForUserID ( $author_user_id, $network, $count, $order_by = "pub_date", $page ); + } + + /** + * Get all the favorited posts of a user. + * @TODO Use $order_by parameter to customize sort order. + * + * @param int $author_user_id + * @param str $network + * @param int $count + * @param str $order_by + * @param int $page + * @param bool $iterator + * Whether or not to return an iterator + * @return s array Post objects + */ + private function getAllFavoritedPostsForUserID($author_user_id, $network, $count, $order_by = "pub_date", $page = 1, $iterator = false) { + // $direction = $direction=="DESC" ? "DESC": "ASC"; + $start_on_record = ($page - 1) * $count; + // order-by information currently hardwired; this will probably change + // if ( !in_array($order_by, $this->REQUIRED_FIELDS) && !in_array($orderaa_by, $this->OPTIONAL_FIELDS )) { + // $order_by="pub_date"; + // } + $q = "SELECT p.*, pub_date - interval #gmt_offset# hour AS adj_pub_date, "; + // TODO: Store favlike_count_cache during Twitter crawl so we don't do this dynamic GROUP BY fakeout + $q .= "count(*) AS favlike_count_cache "; + $q .= "FROM (#prefix#posts p INNER JOIN #prefix#favorites f on f.post_id = p.post_id) "; + $q .= "WHERE p.author_user_id = :author_user_id AND p.network = :network "; + $q .= "GROUP BY p.post_text ORDER BY YEARWEEK(p.pub_date) DESC, favlike_count_cache DESC, p.pub_date DESC "; + $q .= "LIMIT :start_on_record, :limit"; + $vars = array ( + ':author_user_id' => ( string ) $author_user_id, + ':network' => $network, + ':limit' => $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getUsersWhoFavedPost($post_id, $network = 'twitter', $is_public = false) { + $q = "SELECT u.* FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#favorites as f on f.post_id = p.post_id "; + $q .= "INNER JOIN #prefix#users u on f.fav_of_user_id = u.user_id "; + $q .= "WHERE p.network=:network AND f.post_id=:post_id "; + if ($is_public) { + $q .= "AND p.is_protected = 0 "; + } + // could potentially order by follower count instead + $q .= " ORDER BY fav_timestamp desc "; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + return $all_rows; + } + public function getFavoritesFromOneYearAgo($fav_of_user_id, $network, $from_date = null) { + $q = "SELECT p.*, pub_date - interval #gmt_offset# hour AS adj_pub_date "; + $q .= "FROM #prefix#posts p INNER JOIN #prefix#favorites f on f.post_id = p.post_id WHERE f.fav_of_user_id = :fav_of_user_id AND p.network=:network AND p.is_protected = 0 "; - - $vars = array( - ':fav_of_user_id'=> $fav_of_user_id, - ':network'=>$network - ); - if (!isset($from_date)) { - $from_date = 'CURRENT_DATE()'; - } else { - $from_date = "'$from_date'"; - } - $q .= "AND (YEAR(pub_date)!=YEAR(CURRENT_DATE())) "; - $q .= "AND (DAYOFMONTH(pub_date)=DAYOFMONTH($from_date)) AND (MONTH(pub_date)=MONTH($from_date)) "; - $q .= "ORDER BY pub_date DESC LIMIT 5 "; - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - $ps = $this->execute($q, $vars); - $rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - foreach ($rows as $row) { - $posts[] = new Post($row); - } - return $posts; - } - - public function getUsersWhoFavoritedMostOfYourPosts($author_user_id, $network, $last_x_days) { - //$q = "SELECT u.user_name, fav_of_user_id, count(f.post_id) AS total_likes from tu_favorites f "; - $q = "SELECT * FROM ( "; - $q .= "SELECT u.*, count(f.post_id) AS total_likes from #prefix#favorites f "; - $q .= "INNER JOIN #prefix#users u ON u.user_id = f.fav_of_user_id "; - $q .= "INNER JOIN #prefix#posts p ON f.post_id = p.post_id "; - $q .= "WHERE f.author_user_id = :author_user_id and f.network=:network "; - $q .= "AND p.pub_date >= date_sub(current_date, INTERVAL :last_x_days day) "; - $q .= "GROUP BY f.fav_of_user_id ORDER BY total_likes DESC"; - $q .= ") favs WHERE favs.total_likes > 1 LIMIT 3"; - - $vars = array( - ':author_user_id'=> $author_user_id, - ':network'=>$network, - ':last_x_days'=>$last_x_days - ); - - //echo Utils::mergeSQLVars($q, $vars); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - $ps = $this->execute($q, $vars); - $rows = $this->getDataRowsAsArrays($ps); - $users = array(); - foreach ($rows as $row) { - $user = new User($row); - $user->total_likes = $row['total_likes']; - $users[] = $user; - } - return $users; - } + + $vars = array ( + ':fav_of_user_id' => $fav_of_user_id, + ':network' => $network + ); + if (! isset ( $from_date )) { + $from_date = 'CURRENT_DATE()'; + } else { + $from_date = "'$from_date'"; + } + $q .= "AND (YEAR(pub_date)!=YEAR(CURRENT_DATE())) "; + $q .= "AND (DAYOFMONTH(pub_date)=DAYOFMONTH($from_date)) AND (MONTH(pub_date)=MONTH($from_date)) "; + $q .= "ORDER BY pub_date DESC LIMIT 5 "; + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + foreach ( $rows as $row ) { + $posts [] = new Post ( $row ); + } + return $posts; + } + public function getUsersWhoFavoritedMostOfYourPosts($author_user_id, $network, $last_x_days) { + // $q = "SELECT u.user_name, fav_of_user_id, count(f.post_id) AS total_likes from tu_favorites f "; + $q = "SELECT * FROM ( "; + $q .= "SELECT u.*, count(f.post_id) AS total_likes from #prefix#favorites f "; + $q .= "INNER JOIN #prefix#users u ON u.user_id = f.fav_of_user_id "; + $q .= "INNER JOIN #prefix#posts p ON f.post_id = p.post_id "; + $q .= "WHERE f.author_user_id = :author_user_id and f.network=:network "; + $q .= "AND p.pub_date >= date_sub(current_date, INTERVAL :last_x_days day) "; + $q .= "GROUP BY f.fav_of_user_id ORDER BY total_likes DESC"; + $q .= ") favs WHERE favs.total_likes > 1 LIMIT 3"; + + $vars = array ( + ':author_user_id' => $author_user_id, + ':network' => $network, + ':last_x_days' => $last_x_days + ); + + // echo Utils::mergeSQLVars($q, $vars); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + $users = array (); + foreach ( $rows as $row ) { + $user = new User ( $row ); + $user->total_likes = $row ['total_likes']; + $users [] = $user; + } + return $users; + } + public function getGenderOfFavoriters($post_id) { + $q = "SELECT #prefix#users.gender, COUNT(*) as count_gender FROM #prefix#favorites, #prefix#users "; + $q .= "WHERE #prefix#favorites.post_id = :post_id "; + $q .= "AND #prefix#favorites.fav_of_user_id = #prefix#users.user_id "; + $q .= "GROUP BY #prefix#users.gender"; + + $vars = array ( + ':post_id' => $post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + $gender = array (); + foreach ( $rows as $row ) { + if ($row ['gender'] == "female") + $gender ['female_likes_count'] = $row ['count_gender']; + if ($row ['gender'] == "male") + $gender ['male_likes_count'] = $row ['count_gender']; + } + return $gender; + } + + public function getGenderOfCommenters($post_id) { + $q = "SELECT #prefix#users.gender, COUNT(*) as count_gender FROM #prefix#posts, #prefix#users "; + $q .= "WHERE #prefix#posts.in_reply_to_post_id = :post_id "; + $q .= "AND #prefix#posts.author_user_id = #prefix#users.user_id "; + $q .= "GROUP BY #prefix#users.gender"; + + $vars = array ( + ':post_id' => $post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + $gender = array (); + foreach ( $rows as $row ) { + if ($row ['gender'] == "female") + $gender ['female_comm_count'] = $row ['count_gender']; + if ($row ['gender'] == "male") + $gender ['male_comm_count'] = $row ['count_gender']; + } + return $gender; + } + + public function getBirthdayOfFavoriters($post_id) { + $q = "SELECT #prefix#users.birthday as birthday FROM #prefix#favorites, #prefix#users "; + $q .= "WHERE #prefix#favorites.post_id = :post_id "; + $q .= "AND #prefix#favorites.fav_of_user_id = #prefix#users.user_id "; + + $vars = array ( + ':post_id' => $post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + $age = array (); + foreach ( $rows as $row ) { + $age[] = $row ['birthday']; + } + return $age; + } + + public function getBirthdayOfCommenters($post_id) { + $q = "SELECT #prefix#users.birthday as birthday FROM #prefix#posts, #prefix#users "; + $q .= "WHERE #prefix#posts.in_reply_to_post_id = :post_id "; + $q .= "AND #prefix#posts.author_user_id = #prefix#users.user_id "; + + $vars = array ( + ':post_id' => $post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + $age = array (); + foreach ( $rows as $row ) { + $age[] = $row ['birthday']; + } + return $age; + } + + public function getLocationOfFavoriters($post_id) { + $q = "SELECT #prefix#users.user_id as id, #prefix#users.user_name as name, "; + $q .= "#prefix#users.location FROM #prefix#favorites, #prefix#users "; + $q .= "WHERE #prefix#favorites.post_id = :post_id "; + $q .= "AND #prefix#favorites.fav_of_user_id = #prefix#users.user_id "; + $q .= "GROUP BY #prefix#users.user_id "; + + $vars = array ( + ':post_id' => $post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + return $rows; + } + + public function getLocationOfCommenters($post_id) { + $q = "SELECT #prefix#users.user_id as id, #prefix#users.user_name as name, "; + $q .= "#prefix#users.location FROM #prefix#posts, #prefix#users "; + $q .= "WHERE #prefix#posts.in_reply_to_post_id = :post_id "; + $q .= "AND #prefix#posts.author_user_id = #prefix#users.user_id "; + $q .= "GROUP BY #prefix#users.user_id "; + + $vars = array ( + ':post_id' => $post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + return $rows; + } + + public function getGeoOfPostsFromOneWeekAgo($author_user_id, $network='twitter') { + $q = "SELECT #prefix#posts.geo, #prefix#posts.place, #prefix#posts.pub_date + interval #gmt_offset# hour "; + $q .= "AS adj_pub_date FROM #prefix#posts WHERE #prefix#posts.author_user_id = :author_user_id "; + $q .= "AND #prefix#posts.network = :network AND geo<> 'NULL' "; + $q .= "AND (#prefix#posts.pub_date + interval #gmt_offset# hour) >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY) "; + + $vars = array ( + ':author_user_id' => $author_user_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $rows = $this->getDataRowsAsArrays ( $ps ); + return $rows; + } } diff --git a/webapp/_lib/dao/class.PostMySQLDAO.php b/webapp/_lib/dao/class.PostMySQLDAO.php index c25f9b8ee8..ea4848c510 100644 --- a/webapp/_lib/dao/class.PostMySQLDAO.php +++ b/webapp/_lib/dao/class.PostMySQLDAO.php @@ -29,2652 +29,2886 @@ * @copyright 2009-2013 Gina Trapani, Mark Wilkie * @author Gina Trapani */ -class PostMySQLDAO extends PDODAO implements PostDAO { - - /** - * The minimum number of characters required for fulltext queries. - * @var int - */ - const FULLTEXT_CHAR_MINIMUM = 4; - - /** - * Fields required for a post. - * @var array - */ - var $REQUIRED_FIELDS = array('post_id', 'author_username','author_fullname','author_avatar','author_user_id', - 'post_text','is_protected', 'pub_date','source','network'); - - /** - * Optional fields in a post - * @var array - */ - var $OPTIONAL_FIELDS = array('in_reply_to_user_id', 'in_reply_to_post_id','in_retweet_of_post_id', - 'in_rt_of_user_id', 'location', 'place', 'place_id', 'geo', 'retweet_count_cache', - 'retweet_count_api', 'old_retweet_count_cache', 'favlike_count_cache', - 'reply_count_cache', 'is_reply_by_friend', 'is_retweet_by_friend', - 'reply_retweet_distance', 'is_geo_encoded', 'author_follower_count', 'permalink'); - - /** - * Sanitizes an order_by argument to avoid SQL injection and ensure that the table you're ordering by is valid. - * @param string $order_by Column to order on. - * @return string Sanitized column name. If the column was invalid, "pub_date" is returned. - */ - public function sanitizeOrderBy($order_by) { - // some order_by clauses have a table attached to the front of them, remove this for checking. - $sans_table = preg_replace('/^[A-Za-z]\./', '', $order_by); - - // 'retweets' is a add'l query term (it represents a sum of two fields) that is not in the posts table and - // is used in retweet queries for fetching and ordering the retweets. - // If this is the term, just return it, otherwise process normally by checking the fields lists. - if ($order_by == "retweets") { - return $order_by; - } - if ( !in_array($sans_table, $this->REQUIRED_FIELDS) && !in_array($sans_table, $this->OPTIONAL_FIELDS )) { - $order_by="pub_date"; - } - - return $order_by; - } - - public function getPost($post_id, $network, $is_public = false) { - $q = "SELECT u.*, p.*, p.id as post_key, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "LEFT JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = p.network "; - $q .= "WHERE p.post_id=:post_id AND p.network=:network "; - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - $q .= ';'; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $post_row = $this->getDataRowAsArray($ps); - - if ($post_row) { - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".$post_row['id'].")"; - //echo Utils::mergeSQLVars($q, $vars); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine post and links - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $post->author = new User($post_row); - return $post; - } else { - return null; - } - } - - /** - * Add author object to post - * @param array $row - * @return Post post with author member variable set - */ - private function setPostWithAuthor($row) { - $user = new User($row, ''); - $post = new Post($row); - $post->author = $user; - return $post; - } - - public function getRepliesToPost($post_id, $network, $order_by = 'default', $unit = 'km', $is_public = false, - $count= 350, $page = 1) { - $start_on_record = ($page - 1) * $count; - - $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; - $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = :user_network "; - $q .= "WHERE p.network=:network AND in_reply_to_post_id=:post_id "; - if ($is_public) { - $q .= "AND u.is_protected = 0 "; - } - - $class_name = ucfirst($network) . 'Plugin'; - $ordering = @call_user_func($class_name.'::repliesOrdering', $order_by); - if (empty($ordering)) { - $ordering = "pub_date ASC "; - } else { - $ordering .= ", pub_date ASC "; - } - $q .= "ORDER BY " . $ordering. " "; - - if ($count > 0) { - $q .= "LIMIT :start_on_record, :limit;"; - } else { - $q .= ';'; - } - - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':user_network'=>($network == 'facebook page') ? 'facebook' : $network - ); - - if ($count > 0) { - $vars[':limit'] = (int)$count; - $vars['start_on_record'] = (int)$start_on_record; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - - $replies = array(); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $location = array(); - foreach ($all_post_rows as $post_row) { - if ($post_row['is_geo_encoded'] == 1) { - $post_row['short_location'] = $this->processLocationRows($post_row['location']); - if ($unit == 'mi') { - $post_row['reply_retweet_distance'] = - $this->calculateDistanceInMiles($post_row['reply_retweet_distance']); - } - } - - $post = new Post($post_row); - $user = new User($post_row, ''); - $post->author = $user; - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $replies[] = $post; - } - } - return $replies; - } - - public function getRepliesToPostInRange($post_id, $network, $from, $until, $order_by = 'default', $unit = 'km', - $is_public = false, $count = 350, $page = 1) { - $start_on_record = ($page - 1) * $count; - - $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; - $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = :user_network "; - $q .= "WHERE p.network=:network AND in_reply_to_post_id=:post_id AND pub_date BETWEEN :from AND :until "; - if ($is_public) { - $q .= "AND u.is_protected = 0 "; - } - - $class_name = ucfirst($network) . 'Plugin'; - $ordering = @call_user_func($class_name.'::repliesOrdering', $order_by); - if (empty($ordering)) { - $ordering = 'pub_date ASC '; - } else { - $ordering .= ', pub_date ASC '; - } - $q .= 'ORDER BY ' . $ordering. ';'; - - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':user_network'=>($network == 'facebook page') ? 'facebook' : $network, - ':from' => $from, - ':until' => $until - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - - $replies = array(); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $location = array(); - foreach ($all_post_rows as $post_row) { - if ($post_row['is_geo_encoded'] == 1) { - $post_row['short_location'] = $this->processLocationRows($post_row['location']); - if ($unit == 'mi') { - $post_row['reply_retweet_distance'] = - $this->calculateDistanceInMiles($post_row['reply_retweet_distance']); - } - } - - $post = new Post($post_row); - $user = new User($post_row, ''); - $post->author = $user; - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $replies[] = $post; - } - } - return $replies; - } - - public function getRepliesToPostIterator($post_id, $network, $order_by = 'default', $unit = 'km', - $is_public = false, $count = 350, $page = 1) { - $start_on_record = ($page - 1) * $count; - - $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; - $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = :user_network "; - $q .= "WHERE p.network=:network AND in_reply_to_post_id=:post_id "; - if ($is_public) { - $q .= "AND p.is_protected = 0 "; - } - if ($order_by == 'location') { - $q .= "ORDER BY geo_status, reply_retweet_distance, is_reply_by_friend DESC, follower_count DESC "; - } else { - $q .= "ORDER BY is_reply_by_friend DESC, follower_count DESC "; - } - $q .= "LIMIT :start_on_record, :limit;"; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':limit'=>(int)$count, - ':user_network'=>($network=='facebook page')?'facebook':$network, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return new PostIterator($ps); - } - - public function getRetweetsOfPost($post_id, $network='twitter', $order_by = 'default', $unit = 'km', - $is_public = false, $count = null, $page = 1) { - $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; - $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users u on p.author_user_id = u.user_id "; - $q .= "WHERE p.network=:network AND in_retweet_of_post_id=:post_id "; - if ($is_public) { - $q .= "AND p.is_protected = 0 "; - } - if ($order_by == 'location') { - $q .= "ORDER BY geo_status, reply_retweet_distance, is_reply_by_friend DESC, follower_count desc "; - } else if ($order_by != 'default') { - $order_by = $this->sanitizeOrderBy($order_by); - $q .= "ORDER BY $order_by DESC "; - } else { - $q .= "ORDER BY is_reply_by_friend DESC, follower_count DESC, p.id DESC "; - } - - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - - if ($count != null && $count > 0) { - $start_on_record = ($page - 1) * $count; - $q .= 'LIMIT :start_on_record, :limit'; - $vars[':limit'] = (int)$count; - $vars[':start_on_record'] = (int)$start_on_record; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - $retweets = array(); - $location = array(); - foreach ($all_rows as $row) { - if ($row['is_geo_encoded'] == 1) { - $row['short_location'] = $this->processLocationRows($row['location']); - if ($unit == 'mi') { - $row['reply_retweet_distance'] = $this->calculateDistanceInMiles($row['reply_retweet_distance']); - } - } - $retweets[] = $this->setPostWithAuthor($row); - } - return $retweets; - } - - public function getRelatedPostsArray($post_id, $network='twitter', $is_public = false, $count = 350, $page = 1, - $geo_encoded_only = true, $include_original_post = true) { - $start_on_record = ($page - 1) * $count; - $q = "SELECT * FROM ( +class PostMySQLDAO extends PDODAO implements PostDAO { + + /** + * The minimum number of characters required for fulltext queries. + * + * @var int + */ + const FULLTEXT_CHAR_MINIMUM = 4; + + /** + * Fields required for a post. + * + * @var array + */ + var $REQUIRED_FIELDS = array ( + 'post_id', + 'author_username', + 'author_fullname', + 'author_avatar', + 'author_user_id', + 'post_text', + 'is_protected', + 'pub_date', + 'source', + 'network' + ); + + /** + * Optional fields in a post + * + * @var array + */ + var $OPTIONAL_FIELDS = array ( + 'in_reply_to_user_id', + 'in_reply_to_post_id', + 'in_retweet_of_post_id', + 'in_rt_of_user_id', + 'location', + 'place', + 'place_id', + 'geo', + 'retweet_count_cache', + 'retweet_count_api', + 'old_retweet_count_cache', + 'favlike_count_cache', + 'shares_count_cache', + 'reply_count_cache', + 'is_reply_by_friend', + 'is_retweet_by_friend', + 'reply_retweet_distance', + 'is_geo_encoded', + 'author_follower_count', + 'permalink' + ); + + /** + * Sanitizes an order_by argument to avoid SQL injection and ensure that the table you're ordering by is valid. + * + * @param string $order_by + * Column to order on. + * @return string Sanitized column name. If the column was invalid, "pub_date" is returned. + */ + public function sanitizeOrderBy($order_by) { + // some order_by clauses have a table attached to the front of them, remove this for checking. + $sans_table = preg_replace ( '/^[A-Za-z]\./', '', $order_by ); + + // 'retweets' is a add'l query term (it represents a sum of two fields) that is not in the posts table and + // is used in retweet queries for fetching and ordering the retweets. + // If this is the term, just return it, otherwise process normally by checking the fields lists. + if ($order_by == "retweets") { + return $order_by; + } + if (! in_array ( $sans_table, $this->REQUIRED_FIELDS ) && ! in_array ( $sans_table, $this->OPTIONAL_FIELDS )) { + $order_by = "pub_date"; + } + + return $order_by; + } + public function getPost($post_id, $network, $is_public = false) { + $q = "SELECT u.*, p.*, p.id as post_key, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "LEFT JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = p.network "; + $q .= "WHERE p.post_id=:post_id AND p.network=:network "; + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + $q .= ';'; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $post_row = $this->getDataRowAsArray ( $ps ); + + if ($post_row) { + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . $post_row ['id'] . ")"; + // echo Utils::mergeSQLVars($q, $vars); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine post and links + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $post->author = new User ( $post_row ); + return $post; + } else { + return null; + } + } + + /** + * Add author object to post + * + * @param array $row + * @return Post post with author member variable set + */ + private function setPostWithAuthor($row) { + $user = new User ( $row, '' ); + $post = new Post ( $row ); + $post->author = $user; + return $post; + } + public function getRepliesToPost($post_id, $network, $order_by = 'default', $unit = 'km', $is_public = false, $count = 350, $page = 1) { + $start_on_record = ($page - 1) * $count; + + $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; + $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = :user_network "; + $q .= "WHERE p.network=:network AND in_reply_to_post_id=:post_id "; + if ($is_public) { + $q .= "AND u.is_protected = 0 "; + } + + $class_name = ucfirst ( $network ) . 'Plugin'; + $ordering = @call_user_func ( $class_name . '::repliesOrdering', $order_by ); + if (empty ( $ordering )) { + $ordering = "pub_date ASC "; + } else { + $ordering .= ", pub_date ASC "; + } + $q .= "ORDER BY " . $ordering . " "; + + if ($count > 0) { + $q .= "LIMIT :start_on_record, :limit;"; + } else { + $q .= ';'; + } + + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':user_network' => ($network == 'facebook page') ? 'facebook' : $network + ); + + if ($count > 0) { + $vars [':limit'] = ( int ) $count; + $vars ['start_on_record'] = ( int ) $start_on_record; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + + $replies = array (); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $location = array (); + foreach ( $all_post_rows as $post_row ) { + if ($post_row ['is_geo_encoded'] == 1) { + $post_row ['short_location'] = $this->processLocationRows ( $post_row ['location'] ); + if ($unit == 'mi') { + $post_row ['reply_retweet_distance'] = $this->calculateDistanceInMiles ( $post_row ['reply_retweet_distance'] ); + } + } + + $post = new Post ( $post_row ); + $user = new User ( $post_row, '' ); + $post->author = $user; + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $replies [] = $post; + } + } + return $replies; + } + public function getRepliesToPostInRange($post_id, $network, $from, $until, $order_by = 'default', $unit = 'km', $is_public = false, $count = 350, $page = 1) { + $start_on_record = ($page - 1) * $count; + + $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; + $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = :user_network "; + $q .= "WHERE p.network=:network AND in_reply_to_post_id=:post_id AND pub_date BETWEEN :from AND :until "; + if ($is_public) { + $q .= "AND u.is_protected = 0 "; + } + + $class_name = ucfirst ( $network ) . 'Plugin'; + $ordering = @call_user_func ( $class_name . '::repliesOrdering', $order_by ); + if (empty ( $ordering )) { + $ordering = 'pub_date ASC '; + } else { + $ordering .= ', pub_date ASC '; + } + $q .= 'ORDER BY ' . $ordering . ';'; + + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':user_network' => ($network == 'facebook page') ? 'facebook' : $network, + ':from' => $from, + ':until' => $until + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + + $replies = array (); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $location = array (); + foreach ( $all_post_rows as $post_row ) { + if ($post_row ['is_geo_encoded'] == 1) { + $post_row ['short_location'] = $this->processLocationRows ( $post_row ['location'] ); + if ($unit == 'mi') { + $post_row ['reply_retweet_distance'] = $this->calculateDistanceInMiles ( $post_row ['reply_retweet_distance'] ); + } + } + + $post = new Post ( $post_row ); + $user = new User ( $post_row, '' ); + $post->author = $user; + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $replies [] = $post; + } + } + return $replies; + } + public function getRepliesToPostIterator($post_id, $network, $order_by = 'default', $unit = 'km', $is_public = false, $count = 350, $page = 1) { + $start_on_record = ($page - 1) * $count; + + $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; + $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id AND u.network = :user_network "; + $q .= "WHERE p.network=:network AND in_reply_to_post_id=:post_id "; + if ($is_public) { + $q .= "AND p.is_protected = 0 "; + } + if ($order_by == 'location') { + $q .= "ORDER BY geo_status, reply_retweet_distance, is_reply_by_friend DESC, follower_count DESC "; + } else { + $q .= "ORDER BY is_reply_by_friend DESC, follower_count DESC "; + } + $q .= "LIMIT :start_on_record, :limit;"; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':limit' => ( int ) $count, + ':user_network' => ($network == 'facebook page') ? 'facebook' : $network, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return new PostIterator ( $ps ); + } + public function getRetweetsOfPost($post_id, $network = 'twitter', $order_by = 'default', $unit = 'km', $is_public = false, $count = null, $page = 1) { + $q = "SELECT u.*, p.*, (CASE p.is_geo_encoded WHEN 0 THEN 9 ELSE p.is_geo_encoded END) AS geo_status, "; + $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users u on p.author_user_id = u.user_id "; + $q .= "WHERE p.network=:network AND in_retweet_of_post_id=:post_id "; + if ($is_public) { + $q .= "AND p.is_protected = 0 "; + } + if ($order_by == 'location') { + $q .= "ORDER BY geo_status, reply_retweet_distance, is_reply_by_friend DESC, follower_count desc "; + } else if ($order_by != 'default') { + $order_by = $this->sanitizeOrderBy ( $order_by ); + $q .= "ORDER BY $order_by DESC "; + } else { + $q .= "ORDER BY is_reply_by_friend DESC, follower_count DESC, p.id DESC "; + } + + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + + if ($count != null && $count > 0) { + $start_on_record = ($page - 1) * $count; + $q .= 'LIMIT :start_on_record, :limit'; + $vars [':limit'] = ( int ) $count; + $vars [':start_on_record'] = ( int ) $start_on_record; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + $retweets = array (); + $location = array (); + foreach ( $all_rows as $row ) { + if ($row ['is_geo_encoded'] == 1) { + $row ['short_location'] = $this->processLocationRows ( $row ['location'] ); + if ($unit == 'mi') { + $row ['reply_retweet_distance'] = $this->calculateDistanceInMiles ( $row ['reply_retweet_distance'] ); + } + } + $retweets [] = $this->setPostWithAuthor ( $row ); + } + return $retweets; + } + public function getRelatedPostsArray($post_id, $network = 'twitter', $is_public = false, $count = 350, $page = 1, $geo_encoded_only = true, $include_original_post = true) { + $start_on_record = ($page - 1) * $count; + $q = "SELECT * FROM ( SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date FROM #prefix#posts p WHERE (in_retweet_of_post_id=:post_id OR in_reply_to_post_id=:post_id) "; - - if ($include_original_post) { - $q .= "UNION + + if ($include_original_post) { + $q .= "UNION SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date FROM #prefix#posts p WHERE p.post_id=:post_id "; - } - $q .= ") s "; - if ($geo_encoded_only) { - $q .= "WHERE is_geo_encoded='1' "; - } - if ($is_public) { - if (!$geo_encoded_only) { - $q .= "WHERE "; - } else { - $q .= "AND "; - } - $q .= "is_protected = '0' "; - } - $q .= "ORDER BY reply_retweet_distance, location, id LIMIT :start_on_record, :limit;"; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':limit'=>(int)$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDataRowsAsArrays($ps); - } - - public function getRelatedPosts($post_id, $network='twitter', $is_public = false, $count = 350, $page = 1, - $geo_encoded_only = true, $include_original_post = true) { - $all_rows = $this->getRelatedPostsArray($post_id, $network, $is_public, $count, $page, $geo_encoded_only, - $include_original_post); - $responses = array(); - $location = array(); - foreach ($all_rows as $row) { - if ($row['is_geo_encoded'] == 1) { - $row['short_location'] = $this->processLocationRows($row['location']); - } - $responses[] = new Post($row); - } - return $responses; - } - - /** - * @TODO: Figure out a better way to do this, only returns 1-1 exchanges, not back-and-forth threads - */ - public function getPostsAuthorHasRepliedTo($author_id, $count, $network = 'twitter', $page=1, $public_only=true) { - $start_on_record = ($page - 1) * $count; - - $q = "SELECT p1.author_username as questioner_username, p1.author_avatar as questioner_avatar, "; - $q .= "p2.follower_count as answerer_follower_count, p1.post_id as question_post_id, "; - $q .= "p1.post_text as question, p1.pub_date + interval #gmt_offset# hour as question_adj_pub_date, "; - $q .= "p.post_id as answer_post_id, p.author_username as answerer_username, "; - $q .= "p1.is_protected as question_is_protected, p.is_protected as answer_is_protected, "; - $q .= "p.author_avatar as answerer_avatar, p3.follower_count as questioner_follower_count, "; - $q .= "p.post_text as answer, p.network, p.pub_date + interval #gmt_offset# hour as answer_adj_pub_date "; - $q .= "FROM #prefix#posts p INNER JOIN #prefix#posts p1 on p1.post_id = p.in_reply_to_post_id "; - $q .= "JOIN #prefix#users p2 on p2.user_id = :author_id "; - $q .= "JOIN #prefix#users p3 on p3.user_id = p.in_reply_to_user_id "; - $q .= "WHERE p.author_user_id = :author_id AND p.network=:network AND p.in_reply_to_post_id IS NOT null "; - if ($public_only) { - $q .= "AND p.is_protected = 0 AND p1.is_protected = 0 "; - } - $q .= "ORDER BY p.pub_date desc LIMIT :start_on_record, :limit;"; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network, - ':start_on_record'=>(int)$start_on_record, - ':limit'=>(int)$count - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - $posts_replied_to = array(); - foreach ($all_rows as $row) { - $posts_replied_to[] = $row; - } - return $posts_replied_to; - } - - public function getExchangesBetweenUsers($author_id, $other_user_id, $network='twitter') { - $q = "SELECT p1.author_username as questioner_username, p1.author_avatar as questioner_avatar, "; - $q .= "p2.follower_count as questioner_follower_count, p1.post_id as question_post_id, "; - $q .= "p1.post_text as question, p1.pub_date + interval #gmt_offset# hour as question_adj_pub_date, "; - $q .= "p.post_id as answer_post_id, p.author_username as answerer_username, "; - $q .= "p.author_avatar as answerer_avatar, p3.follower_count as answerer_follower_count, "; - $q .= "p.post_text as answer, p.network, p.pub_date + interval #gmt_offset# hour as answer_adj_pub_date "; - $q .= "FROM #prefix#posts p INNER JOIN #prefix#posts p1 on p1.post_id = p.in_reply_to_post_id "; - $q .= "JOIN #prefix#users p2 on p2.user_id = :author_id "; - $q .= "JOIN #prefix#users p3 on p3.user_id = :other_user_id "; - $q .= "WHERE p.in_reply_to_post_id is not null AND p.network=:network AND "; - $q .= "(p.author_user_id = :author_id AND p1.author_user_id = :other_user_id) "; - $q .= "OR (p1.author_user_id = :author_id AND p.author_user_id = :other_user_id) "; - $q .= "ORDER BY answer_adj_pub_date DESC, question_adj_pub_date ASC "; - $vars = array( - ':author_id'=>(string)$author_id, - ':other_user_id'=>(string)$other_user_id, - ':network'=>$network - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - - $all_rows = $this->getDataRowsAsArrays($ps); - $posts_replied_to = array(); - foreach ($all_rows as $row) { - $posts_replied_to[] = $row; - } - return $posts_replied_to; - } - - public function isPostInDB($post_id, $network) { - $q = "SELECT post_id FROM #prefix#posts "; - $q .= "WHERE post_id = :post_id AND network=:network;"; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDataIsReturned($ps); - } - - public function isReplyInDB($post_id, $network) { - return $this->isPostInDB($post_id, $network); - } - - /** - * Increment reply cache count - * @param int $post_id - * @param str $network - * @return int Number of updated rows (1 if successful, 0 if not) - */ - private function incrementReplyCountCache($post_id, $network) { - return $this->incrementCacheCount($post_id, $network, "reply"); - } - - /** - * Update retweet count value from the Twitter API for 'new-style' retweets. - * @param int $post_id - * @param int $retweet_count_api - * @param str $network - * @return int Number of affected rows - */ - private function updateAPIRetweetCount($post_id, $retweet_count_api, $network) { - $q = "UPDATE #prefix#posts SET retweet_count_api = :count "; - $q .= "WHERE post_id = :post_id AND network=:network"; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':count' => $retweet_count_api - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - /** - * Update the 'in_retweet_of_post_id' field for an existing post. This will be done only if - * this field is not already set, which could be the case if the post was originally processed via - * the stream processing scripts. - * @param int $post_id - * @param int $in_retweet_of_post_id - * @param str $network - * @return int Number of affected rows - */ - private function updateInRetweetOfPostID($post_id, $in_retweet_of_post_id, $network) { - $q = "UPDATE #prefix#posts SET in_retweet_of_post_id = :rpid "; - $q .= "WHERE post_id = :post_id AND network=:network "; - $q .= "AND in_retweet_of_post_id IS NULL "; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':rpid' => (string)$in_retweet_of_post_id - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - /** - * Increment retweet cache count, for 'old-style' retweets. - * @param int $post_id - * @param str $network - * @return int number of updated rows (1 if successful, 0 if not) - */ - private function incrementRepostCountCache($post_id, $network) { - return $this->incrementCacheCount($post_id, $network, "old_retweet"); - } - - /** - * Increment retweet cache count, for new-style/native retweets. - * @param int $post_id - * @param str $network - * @return int number of updated rows (1 if successful, 0 if not) - */ - private function incrementNativeRTCountCache($post_id, $network) { - return $this->incrementCacheCount($post_id, $network, "native_retweet"); - } - - /** - * Increment either reply_count_cache, old_retweet_count_cache, retweet_count_cache, or favlike_count_cache - * @param int $post_id - * @param str $network - * @param str $fieldname either "reply" , "old_retweet" (old-style retweets), "native_retweet" or "favlike" - * @return int Number of updated rows - */ - private function incrementCacheCount($post_id, $network, $post_type) { - $fieldname = null; - if ($post_type == "reply") { - $fieldname = "reply"; - } elseif ($post_type == "native_retweet") { - $fieldname = "retweet"; - } elseif ($post_type == "old_retweet") { - $fieldname = "old_retweet"; - } else if ($post_type == "favlike") { - $fieldname ="favlike"; - } - if ($fieldname) { - $q = "UPDATE #prefix#posts SET ".$fieldname."_count_cache = ".$fieldname."_count_cache + 1 "; - $q .= "WHERE post_id = :post_id AND network=:network"; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } else { - // log error -- unknown field name - $this->logger->logError("unknown field name in incrementCacheCount with $post_id, $post_type", - __METHOD__.','.__LINE__); - return null; - } - } - - /** - * Checks to see if the $vals array contains all the required fields to insert a post - * @param array $vals - * @return bool - */ - private function hasAllRequiredFields($vals) { - $result = true; - foreach ($this->REQUIRED_FIELDS as $field) { - if ( !isset($vals[$field]) ) { - $this->logger->logError("Missing post $field value", __METHOD__.','.__LINE__); - $result = false; - } - } - return $result; - } - - public function addPostAndAssociatedInfo(array $vals, $entities = null, $user_array = null) { - $urls = null; - // first add post - $new_post_key = $this->addPost($vals); - // if post did not already exist - if ($new_post_key) { - if ($user_array) { - $u = new User($user_array); - $user_dao = DAOFactory::getDAO('UserDAO'); - $user_dao->setLoggerInstance($this->logger); - $user_dao->updateUser($u); - } - - if ($entities && isset($entities['urls'])) { - $urls = $entities['urls']; - } - URLProcessor::processPostURLs($vals['post_text'], $vals['post_id'], 'twitter', $this->logger, $urls); - - if (isset($entities)) { - if (isset($entities['mentions'])) { - $mention_dao = DAOFactory::getDAO('MentionDAO'); - $mention_dao->setLoggerInstance($this->logger); - $mention_dao->insertMentions($entities['mentions'], $vals['post_id'], $vals['author_user_id'], - $vals['network']); - } - if (isset($entities['hashtags'])) { - $hashtagpost_dao = DAOFactory::getDAO('HashtagPostDAO'); - $hashtagpost_dao->setLoggerInstance($this->logger); - $hashtagpost_dao->insertHashtagPosts($entities['hashtags'], $vals['post_id'], $vals['network']); - } - - if (isset($entities['place'])) { - $place = $entities['place']; - if ($place) { - $place_dao = DAOFactory::getDAO('PlaceDAO'); - $place_dao->setLoggerInstance($this->logger); - $place_dao->insertPlace($place, $vals['post_id'], $vals['network']); - } - } - } - } - return $new_post_key; - } - - public function addPost($vals) { - $vals['post_id'] = (string)$vals['post_id']; - $retweeted_post_data = null; - // first, check to see if this is a retweet, with the original post available. - if (isset($vals['retweeted_post'])) { - // $this->logger->logDebug("this is a retweet- first processing original.", __METHOD__.','.__LINE__); - $retweeted_post_data = $vals['retweeted_post']['content']; - // it turns out that for a native retweet, Twitter may not reliably update the count stored in the - // original-- there may be a lag. So, if there is a first retweet, the original post still - // may show 0 rts. This is less common w/ the REST API than the streaming API, but does not hurt to - // address it anyway. So, if we know there was a retweet, but the rt count is showing 0, set it to 1. - // We know it is at least 1. - if (isset($retweeted_post_data['retweet_count_api']) && ($retweeted_post_data['retweet_count_api'] == 0 )) { - $retweeted_post_data['retweet_count_api'] = 1; - } - // since this was a retweet, process the original post first. - if (isset($vals['retweeted_post']['entities'])) {// REST API won't set this - $retweeted_post_entities = $vals['retweeted_post']['entities']; - } else { - $retweeted_post_entities = null; - } - if (isset($vals['retweeted_post']['user_array'])) {// REST API won't set this - $retweeted_post_user_array = $vals['retweeted_post']['user_array']; - } else { - $retweeted_post_user_array = null; - } - $this->addPostAndAssociatedInfo($retweeted_post_data, $retweeted_post_entities, - $retweeted_post_user_array); - } - - if ($this->hasAllRequiredFields($vals)) { - //process location information - if (!isset($vals['location']) && !isset($vals['geo']) && !isset($vals['place'])) { - $vals['is_geo_encoded'] = 6; - } - //process reply - if (isset($vals['in_reply_to_post_id']) && $vals['in_reply_to_post_id'] != '') { - $vals['in_reply_to_post_id'] = (string) $vals['in_reply_to_post_id']; - $replied_to_post = $this->getPost($vals['in_reply_to_post_id'], $vals['network']); - if (isset($replied_to_post)) { - $this->logger->logInfo("Found reply.", __METHOD__.','.__LINE__); - //check if reply author is followed by the original post author - $follow_dao = DAOFactory::getDAO('FollowDAO'); - if ($follow_dao->followExists($vals['author_user_id'], $replied_to_post->author_user_id, - $replied_to_post->network)) { - $vals['is_reply_by_friend'] = 1; - $this->logger->logInfo("Found reply by a friend!", __METHOD__.','.__LINE__); - } - } - } - //process retweet - if (isset($vals['in_retweet_of_post_id']) && $vals['in_retweet_of_post_id'] != '') { - $vals['in_retweet_of_post_id'] = (string) $vals['in_retweet_of_post_id']; - if (isset($retweeted_post_data)) { - // don't need database retrieval to get the necessary data-- use attached orig post info - $retweeted_post = $retweeted_post_data; - $orig_author_id = $retweeted_post_data['author_user_id']; - $orig_network = $retweeted_post_data['network']; - } else { - // do database query - $retweeted_post = $this->getPost($vals['in_retweet_of_post_id'], $vals['network']); - if (isset($retweeted_post)) { - $orig_author_id = $retweeted_post->author_user_id; - $orig_network = $retweeted_post->network; - } - } - if (isset($orig_author_id) && isset($orig_network)) { - $follow_dao = DAOFactory::getDAO('FollowDAO'); - if ($follow_dao->followExists($vals['author_user_id'], $orig_author_id, $orig_network)) { - $vals['is_retweet_by_friend'] = 1; - $this->logger->logInfo("Found retweet by a friend!", __METHOD__.','.__LINE__); - } - } - } - //SQL variables to bind - $vars = array(); - //SQL query - $q = "INSERT IGNORE INTO #prefix#posts SET "; - //Set up required fields - foreach ($this->REQUIRED_FIELDS as $field) { - $q .= $field."=:".$field.", "; - $vars[':'.$field] = $vals[$field]; - } - //Set up any optional fields - foreach ($this->OPTIONAL_FIELDS as $field) { - if (isset($vals[$field]) && $vals[$field] != '') { - $q .= "".$field."=:".$field.", "; - $vars[':'.$field] = $vals[$field]; - } - } - //Trim off that last comma and space - $q = substr($q, 0, (strlen($q)-2)); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $res = $this->getInsertId($ps); - - if (isset($vals['retweet_count_api']) && ($vals['retweet_count_api'] > 0 ) && !$res) { - // then the post already existed in database & has RT count > 0, so just update the retweet count. - $this->updateAPIRetweetCount($vals['post_id'], $vals['retweet_count_api'], $vals['network']); - } - if (isset($vals['favlike_count_cache']) && ($vals['favlike_count_cache'] > 0 ) && !$res) { - // then the post already existed in database & has fav count > 0, so just update the fav count. - $this->updateFavLikeCount($vals['post_id'], $vals['network'], $vals['favlike_count_cache'] ); - } - // if the post was already in the database, but its 'in_retweet_of_post_id' field was not set in the - // earlier insert, then we need to update the existing post to set that info, then increment the old-style - // retweet cache count for the original post it references as well. This situation can arise if the - // stream processor has already seen and saved the old-style retweet (the stream processor won't have - // the in_retweet_of_post_id information for old-style RTs, so a post may first just be treated as a - // mention, but the crawler may later ID it as a RT.). - if (!$res && !isset($retweeted_post_data) && isset($vals['in_retweet_of_post_id']) && - $this->updateInRetweetOfPostID($vals['post_id'], $vals['in_retweet_of_post_id'], $vals['network'])) { - $this->incrementRepostCountCache($vals['in_retweet_of_post_id'], $vals['network']); - $status_message = "Repost of ".$vals['in_retweet_of_post_id']." by ".$vals["author_username"]. - " ID: ".$vals["post_id"]."; updating old-style retweet cache count"; - $this->logger->logInfo($status_message, __METHOD__.','.__LINE__); - } - //otherwise, only update the other post records if insert went through. - //This avoids incorrect counts in case of attempt to insert dup. - if ($res) { - if (isset($replied_to_post)) { - $this->incrementReplyCountCache($vals['in_reply_to_post_id'], $vals['network']); - $status_message = "Reply found for ".$vals['in_reply_to_post_id'].", ID: ".$vals["post_id"]. - "; updating reply cache count"; - $this->logger->logInfo($status_message, __METHOD__.','.__LINE__); - } - if (isset($retweeted_post)) { - if (!isset($retweeted_post_data)) { // if not a native retweet - $this->incrementRepostCountCache($vals['in_retweet_of_post_id'], $vals['network']); - $status_message = "Repost of ".$vals['in_retweet_of_post_id']." by ".$vals["author_username"]. - " ID: ".$vals["post_id"]."; updating old-style retweet cache count"; - $this->logger->logInfo($status_message, __METHOD__.','.__LINE__); - } else { // native retweet - $count = $this->incrementNativeRTCountCache($vals['in_retweet_of_post_id'], $vals['network']); - $status_message = "Retweet of ".$vals['in_retweet_of_post_id']." by ". - $vals["author_username"]. " ID: ".$vals["post_id"]; - if ($count > 0) { - $status_message .= "; Updated native retweet cache count sucessfully."; - } else { - $status_message .= "; Attempt to update native retweet cache count failed"; - } - $this->logger->logInfo($status_message, __METHOD__.','.__LINE__); - } - } - } - return $res; - } else { - $status_message = "Could not insert post ID ".$vals["post_id"].", missing values"; - $this->logger->logError($status_message, __METHOD__.','.__LINE__); - //doesn't have all req'd values - return false; - } - } - - public function getAllPosts($author_id, $network, $count, $page=1, $include_replies=true, - $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { - return $this->getAllPostsByUserID($author_id, $network, $count, $order_by, $direction, $include_replies, $page, - false, $is_public); - } - - public function getAllPostsIterator($author_id, $network, $count, $include_replies=true, - $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { - return $this->getAllPostsByUserID($author_id, $network, $count, $order_by, $direction, $include_replies, 1, - $iterator = true, $is_public); - } - - /** - * Iterator wrapper for getPostsByFriends - */ - public function getPostsByFriendsIterator($user_id, $network, $count, $is_public=false) { - return $this->getPostsByFriends($user_id, $network, $count, 1, $is_public, true); - } - - public function getPostsByFriends($user_id, $network, $count = 15, $page = 1, $is_public = false, - $iterator = false) { - - $start_on_record = ($page - 1) * $count; - if ($is_public) { - $protected = 'AND p.is_protected = 0 '; - } else { - $protected = ''; - } - - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour AS adj_pub_date "; - $q .= "FROM #prefix#posts AS p "; - $q .= "WHERE p.network = :network "; - $q .= $protected; - $q .= "AND p.author_user_id IN ( "; - $q .= " SELECT user_id FROM #prefix#follows AS f "; - $q .= " WHERE f.follower_id=:user_id AND f.active=1 AND f.network=:network "; - $q .= ")"; - $q .= "ORDER BY p.id DESC "; - $q .= "LIMIT :start_on_record, :limit"; - $vars = array( - ':user_id'=>(string)$user_id, - ':network'=>$network, - ':limit'=>$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - foreach ($all_rows as $row) { - $posts[] = new Post($row); - } - return $posts; - } - - public function getPostsToUserIterator($user_id, $network, $count, $is_public=false) { - return $this->getPostsToUser($user_id, $network, $count, 1, $is_public, true); - } - - public function getPostsToUser($user_id, $network, $count = 15, $page = 1, $is_public = false, $iterator = false) { - $start_on_record = ($page - 1) * $count; - if ($is_public) { - $protected = 'AND p.is_protected = 0 '; - } else { - $protected = ''; - } - - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour AS adj_pub_date "; - $q .= "FROM #prefix#posts AS p "; - $q .= "WHERE p.network = :network "; - $q .= $protected; - $q .= "AND in_reply_to_post_id IS NULL "; - $q .= "AND p.in_reply_to_user_id = :user_id "; - $q .= "ORDER BY p.id DESC "; - $q .= "LIMIT :start_on_record, :limit"; - $vars = array( - ':user_id'=>(string)$user_id, - ':network'=>$network, - ':limit'=>$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllQuestionPosts($author_id, $network, $count, $page=1, $order_by = 'pub_date', - $direction = 'DESC', $is_public = false) { - $start_on_record = ($page - 1) * $count; - - $order_by = $this->sanitizeOrderBy($order_by); - - $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; - - if ($is_public) { - $protected = 'AND p.is_protected = 0'; - } else { - $protected = ''; - } - - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date FROM ( SELECT * "; - $q .= "FROM #prefix#posts p "; - $q .= "WHERE p.author_user_id = :author_id AND p.network=:network "; - $q .= "AND (in_reply_to_post_id IS null OR in_reply_to_post_id = 0) $protected) AS p "; - $q .= "WHERE post_text RLIKE :format1 OR post_text like :format2 "; - $q .= "ORDER BY " . $order_by. ' ' . $direction . ' '; - $q .= "LIMIT :start_on_record, :limit"; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network, - ':format1'=>"\\?$", - ':format2'=>"%\\? %", - ':limit'=>(int)$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllQuestionPostsInRange($author_id, $network, $count, $from, $until, $page=1, - $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { - - $order_by = $this->sanitizeOrderBy($order_by); - - $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; - - if ($is_public) { - $protected = 'AND p.is_protected = 0'; - } else { - $protected = ''; - } - - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date FROM ( SELECT * "; - $q .= "FROM #prefix#posts p "; - $q .= "WHERE p.author_user_id = :author_id AND p.network=:network AND pub_date BETWEEN :from AND :until "; - $q .= "AND (in_reply_to_post_id IS null OR in_reply_to_post_id = 0) $protected) AS p "; - $q .= "WHERE post_text RLIKE :format1 OR post_text like :format2 "; - $q .= "ORDER BY " . $order_by. ' ' . $direction . ' '; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network, - ':format1'=>"\\?$", - ':format2'=>"%\\? %", - ':from' => $from, - ':until' => $until - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - /** - * Get all posts by a given user with configurable order by field and direction - * @param int $author_id - * @param str $network - * @param int $count - * @param str $order_by field name - * @param str $direction either "DESC" or "ASC - * @param bool $include_replies If true, return posts with in_reply_to_post_id set, if not don't - * @param int $page Page number, defaults to 1 - * @param bool $iterator Specify whether or not you want a post iterator returned. Default to - * false. - * @param bool $is_public Whether or not these results are going to be displayed publicly. Defaults to false. - * @return array Posts with link object set - */ - private function getAllPostsByUserID($author_id, $network, $count, $order_by="pub_date", $direction="DESC", - $include_replies=true, $page=1, $iterator=false, $is_public = false) { - $direction = $direction=="DESC" ? "DESC": "ASC"; - $start_on_record = ($page - 1) * $count; - $order_by = $this->sanitizeOrderBy($order_by); - - // if the 'order_by' string is 'retweets', add an add'l aggregate (sum of two fields) var to the select, - // which we can then sort on. - if ($order_by == 'retweets') { - $q = "SELECT p.*, (p.retweet_count_cache + p.old_retweet_count_cache) as retweets, ". - "pub_date + interval #gmt_offset# hour as adj_pub_date "; - } else { - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - } - $q .= "FROM #prefix#posts p "; - $q .= "WHERE author_user_id = :author_id AND p.network=:network "; - if (!$include_replies) { - $q .= "AND (in_reply_to_post_id IS null OR in_reply_to_post_id = 0) "; - } - if ($order_by == 'reply_count_cache') { - $q .= "AND reply_count_cache > 0 "; - } - if ($order_by == 'retweets') { - $q .= "AND (p.retweet_count_cache + p.old_retweet_count_cache) > 0 "; - } - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - $q .= "ORDER BY $order_by $direction "; - $vars = array( - ':author_id'=>$author_id, - ':network'=>$network, - ); - if (isset($count) && $count > 0) { - $q .= "LIMIT :start_on_record, :limit"; - $vars[':limit'] = (int)$count; - $vars[':start_on_record'] = (int)$start_on_record; - } - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getHotPosts($author_user_id, $network, $count) { - // Get posts - $q = "SELECT p.*, (p.retweet_count_cache + p.old_retweet_count_cache) as retweets, "; - $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date FROM #prefix#posts p "; - $q .= "WHERE p.author_user_id = :author_user_id AND p.network=:network "; - $q .= "AND (p.reply_count_cache + p.favlike_count_cache + p.retweet_count_cache + p.old_retweet_count_cache) "; - $q .= "> 0 "; - $q .= "AND (p.in_reply_to_post_id IS null OR p.in_reply_to_post_id = 0) "; - $q .= "AND (p.in_reply_to_user_id IS null OR p.in_reply_to_user_id = 0) "; - $q .= "ORDER BY p.pub_date DESC LIMIT :limit"; - $vars = array( - ':author_user_id'=>$author_user_id, - ':network'=>$network, - ':limit'=>$count - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $posts = array(); - $all_post_rows = $this->getDataRowsAsArrays($ps); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getPostsByUserInRange($author_id, $network, $from, $until, $order_by="pub_date", $direction="DESC", - $iterator=false, $is_public = false) { - $direction = $direction=="DESC" ? "DESC": "ASC"; - - $order_by = $this->sanitizeOrderBy($order_by); - - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "WHERE author_user_id = :author_id AND p.network=:network AND pub_date BETWEEN :from AND :until "; - if ($order_by == 'reply_count_cache') { - $q .= "AND reply_count_cache > 0 "; - } - if ($order_by == 'retweet_count_cache') { - $q .= "AND retweet_count_cache > 0 "; - } - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - $q .= "ORDER BY $order_by $direction "; - $vars = array( - ':author_id'=> (string)$author_id, - ':network'=> $network, - ':from' => $from, - ':until' => $until - ); - //echo Utils::mergeSQLVars($q, $vars); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - - if ($iterator) { - return (new PostIterator($ps)); - } - $all_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - if ($all_rows) { - $post_keys_array = array(); - foreach ($all_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - /** - * Get all posts by a given user with configurable order by field and direction - * @param str $author_username - * @param str $network Default "twitter" - * @param int|bool $count False if no limit (ie, return all rows) - * @param str $order_by field name Default "pub_date" - * @return array Posts with link object set - */ - public function getAllPostsByUsernameOrderedBy($author_username, $network="twitter", $count=0, - $order_by="pub_date", $in_last_x_days = 0, $iterator = false, $is_public = false, $since = null) { - $order_by = $this->sanitizeOrderBy($order_by); - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network - ); - // if the 'order_by' string is 'retweets', add an add'l aggregate (sum of two fields) var to the select, - // which we can then sort on. - if ($order_by == 'retweets') { - $q = "SELECT p.*, GREATEST((p.retweet_count_cache + p.old_retweet_count_cache), retweet_count_api) ". - "as retweets, pub_date + interval #gmt_offset# hour as adj_pub_date "; - } else { - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - } - $q .= "FROM #prefix#posts p "; - $q .= "WHERE author_username = :author_username AND p.network = :network "; - - if ($in_last_x_days > 0) { - if ($since == null) { - $q .= "AND pub_date >= DATE_SUB(CURDATE(), INTERVAL :in_last_x_days DAY) "; - } else { - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :in_last_x_days DAY) "; - $vars[':since'] = $since; - } - $vars[':in_last_x_days'] = (int)$in_last_x_days; - } - if ($order_by == 'reply_count_cache') { - $q .= "AND reply_count_cache > 0 "; - } - if ($order_by == 'retweets') { - $q .= "AND (p.retweet_count_cache + p.old_retweet_count_cache) > 0 "; - } - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - $q .= "ORDER BY ".$order_by." DESC "; - if ($count) { - $q .= "LIMIT :limit"; - $vars[':limit'] = (int)$count; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $posts = array(); - $all_post_rows = $this->getDataRowsAsArrays($ps); - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllPostsByUsernameIterator($username, $network, $count = 0) { - return $this->getAllPostsByUsernameOrderedBy($username, $network, $count, null, null, $iterator = true); - } - - public function getAllPostsByUsername($username, $network) { - return $this->getAllPostsByUsernameOrderedBy($username, $network, null, null, null, $iterator = false); - } - - public function getMostRepliedToPostsInLastWeek($username, $network, $count, $is_public = false) { - return $this->getAllPostsByUsernameOrderedBy($username, $network, $count, 'reply_count_cache', 7, - $iterator = false, $is_public); - } - - public function getMostFavedPostsInLastWeek($username, $network, $count, $is_public = false) { - return $this->getAllPostsByUsernameOrderedBy($username, $network, $count, 'favlike_count_cache', 7, - $iterator = false, $is_public); - } - - public function getMostRetweetedPostsInLastWeek($username, $network, $count, $is_public = false) { - return $this->getAllPostsByUsernameOrderedBy($username, $network, $count, 'retweets', 7, - $iterator = false, $is_public); - } - - public function getTotalPostsByUser($author_username, $network) { - $q = "SELECT COUNT(*) as total "; - $q .= "FROM #prefix#posts p "; - $q .= "WHERE author_username = :author_username AND network=:network "; - $q .= "ORDER BY pub_date ASC"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowAsArray($ps); - return $result["total"]; - } - - public function getStatusSources($author_id, $network) { - $q = "SELECT source, count(source) as total "; - $q .= "FROM #prefix#posts WHERE "; - $q .= "author_user_id = :author_id AND network=:network "; - $q .= "GROUP BY source ORDER BY total DESC;"; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDataRowsAsArrays($ps); - } - - public function getAllMentionsIterator($author_username, $count, $network = "twitter", $page=1, $public=false, - $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { - return $this->getMentions($author_username, $count, $network, $iterator = true, $page, $public, $include_rts, - $order_by, $direction); - } - - public function getAllMentions($author_username, $count, $network = "twitter", $page=1, $public=false, - $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { - return $this->getMentions($author_username, $count, $network, $iterator = false, $page, $public, $include_rts, - $order_by, $direction); - } - - private function getMentions($author_username, $count, $network, $iterator, $page=1, $public=false, - $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { - $start_on_record = ($page - 1) * $count; - - $order_by = $this->sanitizeOrderBy($order_by); - - $direction = ($direction == 'DESC') ? 'DESC' : 'ASC'; - - $author_username = '@'.$author_username; - $q = "SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts AS p "; - $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id "; - $q .= "WHERE p.network = :network AND "; - //fulltext search only works for words longer than 4 chars - if ( strlen($author_username) > PostMySQLDAO::FULLTEXT_CHAR_MINIMUM ) { - $q .= "MATCH (`post_text`) AGAINST(:author_username IN BOOLEAN MODE) "; - } else { - $author_username = '%'.$author_username .'%'; - $q .= "post_text LIKE :author_username "; - } - if ($public) { - $q .= "AND u.is_protected = 0 "; - } - if ($include_rts == false) { - $q .= 'AND p.in_retweet_of_post_id IS NULL '; - } - $q .= "ORDER BY $order_by $direction "; - $q .= "LIMIT :start_on_record, :limit;"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':start_on_record'=>(int)$start_on_record, - ':limit'=>(int)$count - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllMentionsInRange($author_username, $count, $network = "twitter", $from, $until, $page=1, - $public=false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { - return $this->getMentionsInRange($author_username, $count, $network, $from, $until, $iterator = false, $page, - $public, $include_rts, $order_by, $direction); - } - - private function getMentionsInRange($author_username, $count, $network, $from, $until, $iterator, $page=1, - $public=false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { - $start_on_record = ($page - 1) * $count; - - $order_by = $this->sanitizeOrderBy($order_by); - - $direction = ($direction == 'DESC') ? 'DESC' : 'ASC'; - - $author_username = '@'.$author_username; - $q = " SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts AS p "; - $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id "; - $q .= "WHERE p.network = :network AND pub_date BETWEEN :from AND :until "; - - if ( strlen($author_username) > PostMySQLDAO::FULLTEXT_CHAR_MINIMUM ) { - $q .= "AND MATCH (`post_text`) AGAINST(:author_username IN BOOLEAN MODE) "; - } else { - $author_username = '%'.$author_username .'%'; - $q .= "AND post_text LIKE :author_username "; - } - if ($public) { - $q .= "AND u.is_protected = 0 "; - } - - if ($include_rts == false) { - $q .= 'AND p.in_retweet_of_post_id IS NULL '; - } - $q .= "ORDER BY $order_by $direction "; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':from' => $from, - ':until' => $until - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($iterator) { - return (new PostIterator($ps)); - } - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllReplies($user_id, $network, $count, $page = 1, $order_by = 'pub_date', $direction = 'DESC', - $is_public = false) { - $start_on_record = ($page - 1) * $count; - - $order_by = $this->sanitizeOrderBy($order_by); - - $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; - - $q = "SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users u ON p.author_user_id = u.user_id "; - $q .= "WHERE in_reply_to_user_id = :user_id AND p.network=:network "; - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - $q .= "ORDER BY $order_by $direction LIMIT :start_on_record, :limit;"; - $vars = array( - ':user_id'=>(string)$user_id, - ':network'=>$network, - ':start_on_record'=>(int)$start_on_record, - ':limit'=>(int)$count - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getAllRepliesInRange($user_id, $network, $count, $from, $until,$page = 1, $order_by = 'pub_date', - $direction = 'DESC', $is_public = false) { - $start_on_record = ($page - 1) * $count; - - $order_by = $this->sanitizeOrderBy($order_by); - - $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; - - $q = "SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users u ON p.author_user_id = u.user_id "; - $q .= "WHERE in_reply_to_user_id = :user_id AND p.network=:network AND pub_date BETWEEN :from AND :until "; - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - $q .= "ORDER BY " . $order_by. ' ' . $direction . ' '; - $vars = array( - ':user_id'=>(string)$user_id, - ':network'=>$network, - ':from' => $from, - ':until' => $until - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - $posts = array(); - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function getMostRepliedToPosts($user_id, $network, $count, $page=1, $is_public = false) { - return $this->getAllPostsByUserID($user_id, $network, $count, "reply_count_cache", "DESC", true, $page, - $iterator = false, $is_public); - } - - public function getMostFavedPosts($user_id, $network, $count, $page=1, $is_public = false) { - return $this->getAllPostsByUserID($user_id, $network, $count, "favlike_count_cache", "DESC", true, $page, - $iterator = false, $is_public); - } - - public function getMostRetweetedPosts($user_id, $network, $count, $page=1, $is_public = false) { - return $this->getAllPostsByUserID($user_id, $network, $count, "retweets", "DESC", true, $page, - $iterator = false, $is_public); - } - - public function getOrphanReplies($username, $count, $network = "twitter", $page = 1) { - $start_on_record = ($page - 1) * $count; - - $username = "@".$username; - $q = "SELECT p.* , u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users u ON u.user_id = p.author_user_id WHERE "; - //fulltext search only works for words longer than 4 chars - if ( strlen($username) > PostMySQLDAO::FULLTEXT_CHAR_MINIMUM ) { - $q .= "MATCH (`post_text`) AGAINST(:username IN BOOLEAN MODE) "; - } else { - $username = '%'.$username .'%'; - $q .= "post_text LIKE :username "; - } - $q .= "AND in_reply_to_post_id is null "; - $q .= "AND in_retweet_of_post_id is null "; - $q .= "AND p.network = :network "; - $q .= "ORDER BY pub_date DESC LIMIT :start_on_record, :limit;"; - $vars = array( - ':username'=>$username, - ':network'=>$network, - ':limit'=>(int)$count, - ':start_on_record'=>(int)$start_on_record - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - $all_posts = array(); - foreach ($all_rows as $row) { - $all_posts[] = $this->setPostWithAuthor($row); - } - return $all_posts; - } - - /** - * Decrement a post's reply_count_cache - * @param int $post_id - * @param str $network - * @return in count of affected rows - */ - private function decrementReplyCountCache($post_id, $network) { - $q = "UPDATE #prefix#posts SET reply_count_cache = reply_count_cache - 1 "; - $q .= "WHERE post_id = :post_id AND network=:network "; - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - public function getStrayRepliedToPosts($author_id, $network) { - $q = "SELECT in_reply_to_post_id FROM #prefix#posts p "; - $q .= "WHERE p.author_user_id=:author_id AND p.network=:network "; - $q .= "AND p.in_reply_to_post_id NOT IN (select post_id from #prefix#posts) "; - $q .= "AND p.in_reply_to_post_id NOT IN (select post_id from #prefix#post_errors);"; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDataRowsAsArrays($ps); - } - - public function getPostsToGeoencode($limit = 5000) { - $q = "SELECT q.post_id, q.location, q.geo, q.place, q.in_reply_to_post_id, q.in_retweet_of_post_id, "; - $q.= "q.is_reply_by_friend, q.is_retweet_by_friend, q.network FROM "; - $q .= "(SELECT * FROM #prefix#posts AS p WHERE "; - $q .= "(p.geo IS NOT null OR p.place IS NOT null OR p.location IS NOT null) "; - $q .= "AND (p.is_geo_encoded='0' OR p.is_geo_encoded='3') "; - $q .= "ORDER BY id DESC LIMIT :limit) AS q ORDER BY q.id "; - $vars = array( - ':limit'=>(int)$limit - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - return $all_rows; - } - - public function setGeoencodedPost($post_id, $network, $is_geo_encoded = 0, $location = null, $geodata = null, - $distance = 0) { - if ($location && $geodata && ($is_geo_encoded>=1 && $is_geo_encoded<=5)) { - $q = "UPDATE #prefix#posts p SET p.location = :location, p.geo = :geo, "; - $q .= "p.reply_retweet_distance = :distance, p.is_geo_encoded = :is_geo_encoded "; - $q .= "WHERE p.post_id = :post_id AND p.network=:network"; - $vars = array( - ':location'=>$location, - ':geo'=>$geodata, - ':distance'=>$distance, - ':is_geo_encoded'=>$is_geo_encoded, - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - } else { - $q = "UPDATE #prefix#posts p SET p.is_geo_encoded = :is_geo_encoded "; - $q .= "WHERE p.post_id = :post_id AND p.network=:network"; - $vars = array( - ':is_geo_encoded'=>$is_geo_encoded, - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - if ($this->getUpdateCount($ps) > 0) { - $logstatus = "Geolocation for $network post $post_id IS_GEO_ENCODED: $is_geo_encoded"; - $this->logger->logInfo($logstatus, __METHOD__.','.__LINE__); - return true; - } else { - $logstatus = "Geolocation for $network post_id=$post_id IS_GEO_ENCODED: $is_geo_encoded not saved"; - $this->logger->logInfo($logstatus, __METHOD__.','.__LINE__); - return false; - } - } - - public function updateAuthorUsername($author_user_id, $network, $author_username) { - $q = "UPDATE #prefix#posts SET author_username = :author_username "; - $q .= "WHERE author_user_id = :author_user_id AND network=:network"; - $vars = array( - ':author_user_id'=>(string)$author_user_id, - ':author_username'=>$author_username, - ':network' => $network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - - public function deletePost($id) { - $q = "DELETE from #prefix#posts WHERE id = :id LIMIT 1"; - $vars = array( - ':id'=>$id, - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - /** - * Extract location specific to city for each post - * @param int $location Location as stored in the database - * @return str short_location - */ - private function processLocationRows($full_location) { - $location = explode (', ', $full_location); - $length = count($location); - if ($length > 3) { - return $location[$length-3].', '.$location[$length-2].', '.$location[$length-1]; - } else { - return $full_location; - } - } - - /** - * Convert Distance in kilometers to miles - * @param int $distance_in_km Distance in KM - * @return int $distance_in_miles - */ - private function calculateDistanceInMiles($distance_in_km) { - $distance_in_miles = round($distance_in_km/1.609); - return $distance_in_miles; - } - - /** - * Calculate how much each client is used by a user on a specific network - * @param int $author_id - * @param string $network - * @return array First element of the returned array is an array of all the clients the user used, ever. - * The second element is an array of the clients used for the last 25 posts. - * Both arrays are sorted by number of use, descending. - */ - public function getClientsUsedByUserOnNetwork($author_id, $network) { - $q = "SELECT COUNT(*) AS num_posts, source "; - $q .= "FROM #prefix#posts "; - $q .= "WHERE author_user_id = :author_id AND network = :network "; - $q .= "GROUP BY source"; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $rows = $this->getDataRowsAsArrays($this->execute($q, $vars)); - $all_time_clients_usage = self::cleanClientsNames($rows); - - $q = "SELECT COUNT(*) AS num_posts, source"; - $q .= " FROM ("; - $q .= " SELECT *"; - $q .= " FROM #prefix#posts "; - $q .= " WHERE author_user_id = :author_id AND network = :network"; - $q .= " ORDER BY pub_date DESC"; - $q .= " LIMIT 25) p "; - $q .= "GROUP BY source"; - $vars = array( - ':author_id'=>(string)$author_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $rows = $this->getDataRowsAsArrays($this->execute($q, $vars)); - $latest_clients_usage = self::cleanClientsNames($rows); - - if (count($latest_clients_usage) == 1 && isset($latest_clients_usage[''])) { - // Plugin doesn't support 'source' - $latest_clients_usage = array(); - } - - return array($all_time_clients_usage, $latest_clients_usage); - } - - /** - * Clean up and sort (by number of use, descending) the source (client) information fetched in - * getClientsUsedByUserOnNetwork. To clean up the clients names, we remove the HTML link tag. - * @param array $rows obtained from the database (as array); columns should be 'num_posts' and 'source' - * @return array Clients names as keys, number of uses as values. - */ - protected static function cleanClientsNames($rows) { - $clients = array(); - foreach ($rows as $row) { - $client_name = preg_replace('@(.+)@i', '\1', $row['source']); - $clients_key = strtolower($client_name); // will merge together strings with different CaSeS - if (!isset($clients[$clients_key])) { - $clients[$clients_key] = array('name'=>$client_name, 'count'=>0); - } - $clients[$clients_key]['count'] += $row['num_posts']; - } - foreach ($clients as $key => $client) { - unset($clients[$key]); - $clients[$client['name']] = $client['count']; - } - arsort($clients); - return $clients; - } - - public function updateFavLikeCount($post_id, $network, $fav_like_count) { - $q = "UPDATE #prefix#posts SET favlike_count_cache=:favlike_count_cache WHERE post_id=:post_id "; - $q .= "AND network=:network;"; - $vars = array( - ':favlike_count_cache'=>$fav_like_count, - ':post_id'=>$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - public function updateReplyCount($post_id, $network, $reply_count) { - $q = "UPDATE #prefix#posts SET reply_count_cache=:reply_count_cache WHERE post_id=:post_id "; - $q .= "AND network=:network;"; - $vars = array( - ':reply_count_cache'=>$reply_count, - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - public function updateRetweetCount($post_id, $network, $retweet_count) { - $q = "UPDATE #prefix#posts SET retweet_count_cache=:retweet_count_cache WHERE post_id=:post_id "; - $q .= "AND network=:network;"; - $vars = array( - ':retweet_count_cache'=>$retweet_count, - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - public function updatePostText($post_id, $network, $post_text) { - $q = "UPDATE #prefix#posts SET post_text=:post_text WHERE post_id=:post_id "; - $q .= "AND network=:network;"; - $vars = array( - ':post_text'=>$post_text, - ':post_id'=>(string)$post_id, - ':network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getUpdateCount($ps); - } - - public function getOnThisDayFlashbackPosts($author_id, $network, $from_date=null) { - $vars = array( - ':author'=> strval($author_id), - ':network'=>$network - ); - if (!isset($from_date)) { - $from_date = 'CURRENT_DATE()'; - } else { - $vars[':from_date'] = $from_date; - $from_date = ':from_date'; - } - $q = "SELECT po.*, po.id AS post_key, po.pub_date + interval #gmt_offset# hour as adj_pub_date, "; - $q .= "pl.place_type, pl.name, pl.full_name, pl.country_code, pl.country, pl.longlat, pl.bounding_box, "; - $q .= "pl.icon, pl.map_image FROM #prefix#posts po "; - $q .= "LEFT JOIN #prefix#places pl ON po.place_id = pl.place_id "; - $q .= "WHERE (YEAR(pub_date)!=YEAR(CURRENT_DATE())) "; - $q .= "AND (DAYOFMONTH(pub_date)=DAYOFMONTH($from_date)) AND (MONTH(pub_date)=MONTH($from_date)) AND "; - $q .= "author_user_id=:author AND po.network=:network AND "; - $q .= "in_reply_to_post_id IS null AND in_reply_to_user_id IS NULL AND "; - $q .= "in_retweet_of_post_id IS NULL AND in_rt_of_user_id IS NULL AND "; - // Don't return posts without post text or place to display - $q .= "(post_text != '' || po.place != '') "; - $q .= "ORDER BY pub_date DESC "; - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_post_rows = $this->getDataRowsAsArrays($ps); - - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['post_key']; - } - - $all_posts = array(); - foreach ($all_post_rows as $row) { - $post = new Post($row); - // Create a place object incase this post has some associated place data - $post->place_obj = new Place($row); - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(",", $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $all_posts[] = $post; - } - return array_reverse($all_posts); - } - - public function getAllCheckins($author_id, $network, $count=15, $page=1) { - // Work out which is the first checkin we want to get - $start_on_record = ($page - 1) * $count; - - // Get the checkins from the database for this user - $q = "SELECT po.id AS post_key, po.post_id, po.author_user_id, po.author_username, po.author_fullname, "; - $q .= "po.author_avatar, po.author_follower_count, po.post_text, po.is_protected, po.source, po.location, "; - $q .= "po.place, po.place_id, po.geo, pub_date, po.in_reply_to_user_id,"; - $q .= "po.in_reply_to_post_id, "; - $q .= "po.reply_count_cache, po.is_reply_by_friend, po.in_retweet_of_post_id, po.old_retweet_count_cache, "; - $q .= "po.is_retweet_by_friend, po.reply_retweet_distance, po.network, po.is_geo_encoded, "; - $q .= "po.in_rt_of_user_id, po.retweet_count_cache, po.retweet_count_api, po.favlike_count_cache, "; - $q .= "po.pub_date + interval #gmt_offset# hour as adj_pub_date, pl.place_id, pl.place_type, pl.name, "; - $q .= "pl.full_name, pl.country_code, pl.country, pl.network, pl.longlat, pl.bounding_box, pl.icon, "; - $q .= "pl.map_image, pl.id "; - $q .= "FROM #prefix#posts po "; - $q .= "JOIN #prefix#places pl ON po.place_id = pl.place_id "; - $q .= "WHERE author_user_id=:author AND po.network=:network AND po.in_reply_to_post_id IS null "; - $q .= "ORDER BY pub_date DESC "; - $vars = array( - ':author'=>$author_id, - ':network'=>$network - ); - if (isset($count) && $count > 0) { - $q .= "LIMIT :start_on_record, :limit"; - $vars[':limit'] = (int)$count; - $vars[':start_on_record'] = (int)$start_on_record; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - - // Get all the post ids of the checkins (we use this later for our link query) - $post_keys_array = array(); - foreach ($all_rows as $row) { - $post_keys_array[] = $row['post_key']; - } - - // An array to store each post object in for the checkins - $all_posts = array(); - foreach ($all_rows as $row) { - $data = new Post($row); - // Create a place object for any place related data - $data->place_obj = new Place($row); - // Query for all the links related to these posts / checkins - $q2 = "SELECT * FROM #prefix#links WHERE post_key in (".implode(",", $post_keys_array).")"; - $ps2 = $this->execute($q2); - $all_link_rows = $this->getDataRowsAsArrays($ps2); - - // For each link returned if it equals the post id of this post add the link to this post - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $data->id) { - $data->addLink(new Link($link_row)); - } - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - // Now we have all the information for this post store it in our array of all posts - $all_posts[] = $data; - } - return $all_posts; - } - - public function countCheckinsToPlaceTypes($author_id, $network) { - $q = "SELECT place_type, COUNT(place_type) AS place_count FROM #prefix#places WHERE place_id IN "; - $q .= "(SELECT place_id FROM #prefix#posts WHERE author_user_id=:author AND network=:network) "; - $q .= "GROUP BY place_type"; - $vars = array( - 'author'=>$author_id, - 'network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - // Convert the results into Google Charts Format - foreach ($all_rows as $row) { - /* Data needs to be in format - {c:[{v: 'Title'}, {v: Value of data for this title}]} - e.g. [{c:[{v: 'Park'}, {v: 5}]} - */ - $resultset[] = array('c' => array( - array('v' => $row['place_type'], 'f' => $row['place_type']), - array('v' => intval($row['place_count'])) - )); - } - - // Set the meta values like titles etc. - $metadata = array( - array('type' => 'string', 'label' => 'Place Type'), - array('type' => 'number', 'label' => 'Number of Checkins to this place type'), - ); - - // Now encode this data as JSON for the Google Charts API - $visdata = json_encode(array('rows' => $resultset, 'cols' => $metadata)); - - if (count($all_rows) > 0) { - return $visdata; - } else { - return ""; - } - } - - public function countCheckinsToPlaceTypesLastWeek($author_id, $network) { - $q = "SELECT place_type, COUNT(place_type) AS place_count FROM #prefix#places WHERE place_id IN "; - $q .= "(SELECT place_id FROM #prefix#posts WHERE author_user_id=:author_id AND network=:network_name "; - $q .= "AND YEARWEEK(pub_date) = YEARWEEK(CURRENT_DATE) AND in_reply_to_post_id IS null ) "; - $q .= "GROUP BY place_type"; - $vars = array( - ':author_id'=>$author_id, - ':network_name'=>$network); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - - // Convert the results into Google Charts Format - foreach ($all_rows as $row) { - /* Data needs to be in format - {c:[{v: 'Title'}, {v: Value of data for this title}]} - e.g. [{c:[{v: 'Park'}, {v: 5}]} - */ - $resultset[] = array('c' => array( - array('v' => $row['place_type'], 'f' => $row['place_type']), - array('v' => intval($row['place_count'])) - )); - } - - // Set the meta values like titles etc. - $metadata = array( - array('type' => 'string', 'label' => 'Place Type'), - array('type' => 'number', 'label' => 'Number of Checkins to this place type'), - ); - - // Now encode this data as JSON for the Google Charts API - $visdata = json_encode(array('rows' => $resultset, 'cols' => $metadata)); - - /* As we are getting posts from the last week, we might not have any so return a blank string in that instance - * So we know not to draw this graph on the dashboard. - */ - - if (count($all_rows) > 0) { - return $visdata; - } else { - return ""; - } - } - - public function getPostsPerHourDataVis($author_id, $network) { - $posts_per_hour_all_time = $this->getCheckinsPerHourAllTime($author_id, $network); - $posts_per_hour_last_week = $this->getCheckinsPerHourLastWeek($author_id, $network); - - // Convert the results into Google Charts Format - $i = 0; - while ($i < 24) { // Hour 0 through 23 - /* Data needs to be in format - {c:[{v: 'Hour'}, {v: Number of checkins at this hour}]} - e.g. [{c:[{v: '11'}, {v: 5}]} - */ - foreach ($posts_per_hour_last_week as $row) { - if ($row['hour'] == $i) { - $last_week_value = intval($row['total']); - break; - } - $last_week_value = 0; - } - - foreach ($posts_per_hour_all_time as $row) { - if ($row['hour'] == $i) { - $all_time_value = intval($row['total']); - break; - } - $all_time_value = 0; - } - - $post_data[] = array('c' => array( - array('v' => $i), - array('v' => $last_week_value), - array('v' => $all_time_value), - )); - $i++; - } - - // Set the meta values like titles etc. - $metadata = array( - array('type' => 'string', 'label' => 'Hour of Day'), - array('type' => 'number', 'label' => 'Checkins Last Week'), - array('type' => 'number', 'label' => 'Checkins All Time'), - ); - - // Now encode this data as JSON for the Google Charts API - return json_encode(array('rows' => $post_data, 'cols' => $metadata)); - } - - private function getCheckinsPerHourAllTime($author_id, $network) { - $q = "SELECT HOUR(pub_date) AS hour, COUNT(HOUR(pub_date)) AS total FROM #prefix#posts "; - $q .= "WHERE author_user_id=:author "; - $q .= "AND network=:network AND in_reply_to_post_id IS null GROUP BY HOUR(pub_date)"; - $vars = array( - 'author'=>$author_id, - 'network'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDataRowsAsArrays($ps); - } - - private function getCheckinsPerHourLastWeek($author_id, $network) { - $q = "SELECT CAST(HOUR(pub_date) AS UNSIGNED) AS hour, COUNT(HOUR(pub_date)) AS total FROM #prefix#posts "; - $q .= "WHERE author_user_id=:author "; - $q .= "AND network=:network AND YEARWEEK(pub_date) = YEARWEEK(CURRENT_DATE) AND in_reply_to_post_id IS null "; - $q .= "GROUP BY HOUR(pub_date)"; - $vars = array( - 'author'=>$author_id, - 'network'=>$network); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDataRowsAsArrays($ps); - } - - public function getAllCheckinsInLastWeekAsGoogleMap($author_id, $network) { - $q = "SELECT geo FROM #prefix#posts WHERE author_user_id=:author_id AND network=:network_name "; - $q .= "AND YEARWEEK(pub_date) = YEARWEEK(CURRENT_DATE) AND in_reply_to_post_id IS null "; - $vars = array( - ':author_id'=>$author_id, - ':network_name'=>$network - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $all_rows = $this->getDataRowsAsArrays($ps); - - $url = 'http://maps.googleapis.com/maps/api/staticmap?size=708x500&maptype=roadmap&markers='; - $url .= 'color:blue%7C'; - - foreach($all_rows as $row) { - $url .= "|".$row['geo']; - } - - $url .= '&sensor=false'; - - if (count($all_rows) > 0) { - return $url; - } else { - return ""; - } - } - - public function getMostPopularPostsOfTheYear($author_user_id, $network, $year, $count=25) { - $vars = array( - ':network_user_id'=> $author_user_id, - ':network'=>$network, - ':year'=>$year, - ':count'=>$count - ); - $q = "SELECT po.*, po.id AS post_key, po.pub_date + interval #gmt_offset# hour as adj_pub_date, "; - $q .= "(greatest(retweet_count_cache, retweet_count_api) + reply_count_cache + favlike_count_cache) "; - $q .= "AS total_responses FROM #prefix#posts po WHERE YEAR(pub_date)=:year "; - $q .= "AND po.author_user_id=:network_user_id AND po.network=:network AND "; - $q .= "in_reply_to_post_id IS null AND in_reply_to_user_id IS NULL AND "; - $q .= "in_retweet_of_post_id IS NULL AND in_rt_of_user_id IS NULL "; - $q .= "ORDER BY total_responses DESC LIMIT :count"; - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $post_rows = $this->getDataRowsAsArrays($ps); - $posts = array(); - foreach ($post_rows as $row) { - $post = new Post($row); - $posts[] = $post; - } - return $posts; - } - public function getAverageRetweetCount($author_username, $network, $last_x_days, $since=null){ - if ($since==null) { - $since = date('Y-m-d'); - } - - $q = "SELECT round(avg(old_retweet_count_cache + retweet_count_cache)) as average_retweet_count "; - $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND (retweet_count_cache > 0 OR old_retweet_count_cache > 0) "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowAsArray($ps); - if (!isset($result["average_retweet_count"])) { - $q = "SELECT round(avg(retweet_count_api)) as average_retweet_count "; - $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND retweet_count_api > 0 "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowAsArray($ps); - } - return $result["average_retweet_count"]; - } - - public function getAverageFaveCount($author_username, $network, $last_x_days, $since=null){ - if ($since==null) { - $since = date('Y-m-d'); - } - - $q = "SELECT round(avg(favlike_count_cache)) as average_favlike_count "; - $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND favlike_count_cache > 0 "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowAsArray($ps); - return $result["average_favlike_count"]; - } - - public function getAverageReplyCount($author_username, $network, $last_x_days, $since=null){ - if ($since==null) { - $since = date('Y-m-d'); - } - - $q = "SELECT round(avg(reply_count_cache)) as average_reply_count "; - $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND reply_count_cache > 0 "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowAsArray($ps); - return $result["average_reply_count"]; - } - - public function doesUserHavePostsWithRetweetsSinceDate($author_username, $network, $last_x_days, $since=null) { - if ($since==null) { - $since = date('Y-m-d'); - } - - $q = "SELECT id FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND (retweet_count_cache > 0 OR old_retweet_count_cache > 0 OR retweet_count_api > 0) "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY) LIMIT 1;"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowsAsArrays($ps); - if (sizeof($result) < 1 ) { - return false; - } else { - return true; - } - } - - public function doesUserHavePostsWithFavesSinceDate($author_username, $network, $last_x_days, $since=null) { - if ($since==null) { - $since = date('Y-m-d'); - } - - $q = "SELECT id FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND (favlike_count_cache > 0) "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY) LIMIT 1;"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowsAsArrays($ps); - if (sizeof($result) < 1 ) { - return false; - } else { - return true; - } - } - - public function doesUserHavePostsWithRepliesSinceDate($author_username, $network, $last_x_days, $since=null) { - if ($since==null) { - $since = date('Y-m-d'); - } - - $q = "SELECT id FROM #prefix#posts WHERE network=:network and author_username=:author_username "; - $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; - $q .= "AND (reply_count_cache > 0) "; - $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY) LIMIT 1;"; - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network, - ':last_x_days'=>(int)$last_x_days, - ':since'=>$since - ); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowsAsArrays($ps); - if (sizeof($result) < 1 ) { - return false; - } else { - return true; - } - } - - public function getRetweetsByAuthorsOverFollowerCount($post_id, $network, $follower_count_threshold) { - $q = "SELECT u.* "; - $q .= "FROM #prefix#posts p "; - $q .= "INNER JOIN #prefix#users u on p.author_user_id = u.user_id "; - $q .= "WHERE p.network=:network AND in_retweet_of_post_id=:post_id "; - $q .= "AND p.is_protected = 0 AND u.follower_count > :follower_count_threshold "; - $q .= "ORDER BY u.follower_count DESC, p.id DESC LIMIT 5"; - - $vars = array( - ':post_id'=>(string)$post_id, - ':network'=>$network, - ':follower_count_threshold'=>$follower_count_threshold - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - - return $this->getDataRowsAsObjects($ps, 'User'); - } - - public function getDaysAgoSinceUserRepliedToRecipient($user_id, $recipient_id, $network) { - $q = "SELECT TIMESTAMPDIFF(DAY, pub_date, NOW()) AS last_reply_days_ago "; - $q .= "FROM #prefix#posts AS p "; - $q .= "WHERE p.author_user_id=:user_id AND p.network=:network AND p.in_reply_to_user_id=:recipient_id "; - $q .= "ORDER BY p.pub_date DESC LIMIT 1"; - - $vars = array( - ':user_id' => (string)$user_id, - ':recipient_id' => (string)$recipient_id, - ':network' => $network - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowsAsArrays($ps); - - return count($result) ? (int)$result[0]['last_reply_days_ago'] : null; - } - - public function countAllPostsByUserSinceDaysAgo($author_id, $network, $days_ago=7) { - $q = "SELECT COUNT(*) AS count FROM #prefix#posts AS p "; - $q .= "WHERE p.author_user_id=:user_id AND p.network=:network "; - $q .= "AND p.pub_date>=DATE_SUB(CURDATE(), INTERVAL :days_ago DAY) "; - $vars = array( - ':user_id'=>$author_id, - ':network'=>$network, - ':days_ago'=>$days_ago - ); - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $result = $this->getDataRowAsArray($ps); - - return (int)$result['count']; - } - - public function searchPostsByUser(array $keywords, $network, $author_username, $page_number=1, $page_count=20) { - if (!is_array($keywords)) { - return null; - } - $start_on_record = ($page_number - 1) * $page_count; - - $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p WHERE author_username=:author_username AND network = :network "; - $q .= "AND ("; - $counter = 0; - $search_terms = array(); - $unique_keywords = array_unique($keywords); - foreach ($unique_keywords as $keyword) { - $term = $keyword; - for ($i = 1; $i < count(array_keys($keywords,$keyword)); $i++) { - $term .= '%'.$keyword; - } - $search_terms[] = $term; - $q .= " post_text LIKE :keyword".$counter." "; - if ($keyword != end($unique_keywords)) { - $q .= "AND"; - } - $counter++; - } - $q .= ") ORDER BY pub_date DESC "; - if ($page_count > 0) { - $q .= "LIMIT :start_on_record, :limit;"; - } else { - $q .= ';'; - } - - $vars = array( - ':author_username'=>$author_username, - ':network'=>$network - ); - $counter = 0; - foreach ($search_terms as $term) { - $vars[':keyword'.$counter] = '%'.$term.'%'; - $counter++; - } - if ($page_count > 0) { - $vars[':limit'] = (int)$page_count; - $vars[':start_on_record'] = (int)$start_on_record; - } - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $post_rows = $this->getDataRowsAsArrays($ps); - - $posts = array(); - foreach ($post_rows as $row) { - $post = new Post($row); - $posts[] = $post; - } - return $posts; - } - - /** - * Get all posts by a given hashtag with configurable order by field and direction - * @param int $hashtag_id - * @param str $network - * @param int $count - * @param str $order_by field name - * @param str $direction either "DESC" or "ASC - * @param int $page Page number, defaults to 1 - * @param bool $is_public Whether or not these results are going to be displayed publicly. Defaults to false. - * @return array Posts with link object set - */ - public function getAllPostsByHashtagId($hashtag_id, $network, $count, $order_by="pub_date", $direction="DESC", - $page=1, $is_public = false) { - - $direction = $direction=="DESC" ? "DESC": "ASC"; - $start_on_record = ($page - 1) * $count; - $order_by = $this->sanitizeOrderBy($order_by); - - $q = "SELECT p.*, p.pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p, #prefix#hashtags_posts hp, #prefix#hashtags h "; - $q .= "WHERE p.post_id= hp.post_id AND hp.hashtag_id = h.id AND h.id = :hashtag_id AND p.network=:network "; - - if ($is_public) { - $q .= 'AND p.is_protected = 0 '; - } - - $q .= "ORDER BY p.$order_by $direction "; - - $vars = array( - ':hashtag_id'=>$hashtag_id, - ':network'=>$network, - ); - - if (isset($count) && $count > 0) { - $q .= "LIMIT :start_on_record, :limit"; - $vars[':limit'] = (int)$count; - $vars[':start_on_record'] = (int)$start_on_record; - } - - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - - $all_post_rows = $this->getDataRowsAsArrays($ps); - - $posts = array(); - - if ($all_post_rows) { - $post_keys_array = array(); - foreach ($all_post_rows as $row) { - $post_keys_array[] = $row['id']; - } - - // Get links - $q = "SELECT * FROM #prefix#links WHERE post_key in (".implode(',', $post_keys_array).")"; - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q); - $all_link_rows = $this->getDataRowsAsArrays($ps); - - // Combine posts and links - foreach ($all_post_rows as $post_row) { - $post = new Post($post_row); - foreach ($all_link_rows as $link_row) { - if ($link_row['post_key'] == $post->id) { - $post->addLink(new Link($link_row)); - } - } - $posts[] = $post; - } - } - return $posts; - } - - public function deletePostsByHashtagId($hashtag_id) { - $q = "DELETE t.* FROM #prefix#posts t "; - $q .= "INNER JOIN #prefix#hashtags_posts hp ON t.post_id = hp.post_id "; - $q .= "WHERE hp.hashtag_id=:hashtag_id;"; - $vars = array(':hashtag_id'=>$hashtag_id); - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - return $this->getDeleteCount($ps); - } - - public function searchPostsByHashtag($keywords, Hashtag $hashtag, $network, $page_number=1, $page_count=20) { - $start_on_record = ($page_number - 1) * $page_count; - $q = "SELECT p.*, p.pub_date + interval #gmt_offset# hour as adj_pub_date "; - $q .= "FROM #prefix#posts p, #prefix#hashtags_posts hp, #prefix#hashtags h "; - $q .= "WHERE p.post_id= hp.post_id AND hp.hashtag_id = h.id AND p.network=:network AND h.id = :hashtag_id "; - if (sizeof($keywords) > 0) { - $q .= "AND ("; - $counter = 0; - foreach ($keywords as $keyword) { - $q .= " post_text LIKE :keyword".$counter." "; - if ($keyword != end($keywords)) { - $q .= "AND"; - } - $counter++; - } - $q .= ") "; - } - $q .= "ORDER BY pub_date DESC "; - if ($page_count > 0) { - $q .= "LIMIT :start_on_record, :limit;"; - } else { - $q .= ';'; - } - $vars = array( - ':network'=>$network, - ':hashtag_id'=>$hashtag->id - ); - if (sizeof($keywords) > 0) { - $counter = 0; - foreach ($keywords as $keyword) { - $vars[':keyword'.$counter] = '%'.$keyword.'%'; - $counter++; - } - } - if ($page_count > 0) { - $vars[':limit'] = (int)$page_count; - $vars[':start_on_record'] = (int)$start_on_record; - } - if ($this->profiler_enabled) { Profiler::setDAOMethod(__METHOD__); } - $ps = $this->execute($q, $vars); - $post_rows = $this->getDataRowsAsArrays($ps); - - $posts = array(); - foreach ($post_rows as $row) { - $post = new Post($row); - $posts[] = $post; - } - return $posts; - } + } + $q .= ") s "; + if ($geo_encoded_only) { + $q .= "WHERE is_geo_encoded='1' "; + } + if ($is_public) { + if (! $geo_encoded_only) { + $q .= "WHERE "; + } else { + $q .= "AND "; + } + $q .= "is_protected = '0' "; + } + $q .= "ORDER BY reply_retweet_distance, location, id LIMIT :start_on_record, :limit;"; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':limit' => ( int ) $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDataRowsAsArrays ( $ps ); + } + public function getRelatedPosts($post_id, $network = 'twitter', $is_public = false, $count = 350, $page = 1, $geo_encoded_only = true, $include_original_post = true) { + $all_rows = $this->getRelatedPostsArray ( $post_id, $network, $is_public, $count, $page, $geo_encoded_only, $include_original_post ); + $responses = array (); + $location = array (); + foreach ( $all_rows as $row ) { + if ($row ['is_geo_encoded'] == 1) { + $row ['short_location'] = $this->processLocationRows ( $row ['location'] ); + } + $responses [] = new Post ( $row ); + } + return $responses; + } + + /** + * @TODO: Figure out a better way to do this, only returns 1-1 exchanges, not back-and-forth threads + */ + public function getPostsAuthorHasRepliedTo($author_id, $count, $network = 'twitter', $page = 1, $public_only = true) { + $start_on_record = ($page - 1) * $count; + + $q = "SELECT p1.author_username as questioner_username, p1.author_avatar as questioner_avatar, "; + $q .= "p2.follower_count as answerer_follower_count, p1.post_id as question_post_id, "; + $q .= "p1.post_text as question, p1.pub_date + interval #gmt_offset# hour as question_adj_pub_date, "; + $q .= "p.post_id as answer_post_id, p.author_username as answerer_username, "; + $q .= "p1.is_protected as question_is_protected, p.is_protected as answer_is_protected, "; + $q .= "p.author_avatar as answerer_avatar, p3.follower_count as questioner_follower_count, "; + $q .= "p.post_text as answer, p.network, p.pub_date + interval #gmt_offset# hour as answer_adj_pub_date "; + $q .= "FROM #prefix#posts p INNER JOIN #prefix#posts p1 on p1.post_id = p.in_reply_to_post_id "; + $q .= "JOIN #prefix#users p2 on p2.user_id = :author_id "; + $q .= "JOIN #prefix#users p3 on p3.user_id = p.in_reply_to_user_id "; + $q .= "WHERE p.author_user_id = :author_id AND p.network=:network AND p.in_reply_to_post_id IS NOT null "; + if ($public_only) { + $q .= "AND p.is_protected = 0 AND p1.is_protected = 0 "; + } + $q .= "ORDER BY p.pub_date desc LIMIT :start_on_record, :limit;"; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network, + ':start_on_record' => ( int ) $start_on_record, + ':limit' => ( int ) $count + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + $posts_replied_to = array (); + foreach ( $all_rows as $row ) { + $posts_replied_to [] = $row; + } + return $posts_replied_to; + } + public function getExchangesBetweenUsers($author_id, $other_user_id, $network = 'twitter') { + $q = "SELECT p1.author_username as questioner_username, p1.author_avatar as questioner_avatar, "; + $q .= "p2.follower_count as questioner_follower_count, p1.post_id as question_post_id, "; + $q .= "p1.post_text as question, p1.pub_date + interval #gmt_offset# hour as question_adj_pub_date, "; + $q .= "p.post_id as answer_post_id, p.author_username as answerer_username, "; + $q .= "p.author_avatar as answerer_avatar, p3.follower_count as answerer_follower_count, "; + $q .= "p.post_text as answer, p.network, p.pub_date + interval #gmt_offset# hour as answer_adj_pub_date "; + $q .= "FROM #prefix#posts p INNER JOIN #prefix#posts p1 on p1.post_id = p.in_reply_to_post_id "; + $q .= "JOIN #prefix#users p2 on p2.user_id = :author_id "; + $q .= "JOIN #prefix#users p3 on p3.user_id = :other_user_id "; + $q .= "WHERE p.in_reply_to_post_id is not null AND p.network=:network AND "; + $q .= "(p.author_user_id = :author_id AND p1.author_user_id = :other_user_id) "; + $q .= "OR (p1.author_user_id = :author_id AND p.author_user_id = :other_user_id) "; + $q .= "ORDER BY answer_adj_pub_date DESC, question_adj_pub_date ASC "; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':other_user_id' => ( string ) $other_user_id, + ':network' => $network + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + + $all_rows = $this->getDataRowsAsArrays ( $ps ); + $posts_replied_to = array (); + foreach ( $all_rows as $row ) { + $posts_replied_to [] = $row; + } + return $posts_replied_to; + } + public function isPostInDB($post_id, $network) { + $q = "SELECT post_id FROM #prefix#posts "; + $q .= "WHERE post_id = :post_id AND network=:network;"; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDataIsReturned ( $ps ); + } + public function isReplyInDB($post_id, $network) { + return $this->isPostInDB ( $post_id, $network ); + } + + /** + * Increment reply cache count + * + * @param int $post_id + * @param str $network + * @return int Number of updated rows (1 if successful, 0 if not) + */ + private function incrementReplyCountCache($post_id, $network) { + return $this->incrementCacheCount ( $post_id, $network, "reply" ); + } + + /** + * Update retweet count value from the Twitter API for 'new-style' retweets. + * + * @param int $post_id + * @param int $retweet_count_api + * @param str $network + * @return int Number of affected rows + */ + private function updateAPIRetweetCount($post_id, $retweet_count_api, $network) { + $q = "UPDATE #prefix#posts SET retweet_count_api = :count "; + $q .= "WHERE post_id = :post_id AND network=:network"; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':count' => $retweet_count_api + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + + /** + * Update the 'in_retweet_of_post_id' field for an existing post. + * This will be done only if + * this field is not already set, which could be the case if the post was originally processed via + * the stream processing scripts. + * + * @param int $post_id + * @param int $in_retweet_of_post_id + * @param str $network + * @return int Number of affected rows + */ + private function updateInRetweetOfPostID($post_id, $in_retweet_of_post_id, $network) { + $q = "UPDATE #prefix#posts SET in_retweet_of_post_id = :rpid "; + $q .= "WHERE post_id = :post_id AND network=:network "; + $q .= "AND in_retweet_of_post_id IS NULL "; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':rpid' => ( string ) $in_retweet_of_post_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + + /** + * Increment retweet cache count, for 'old-style' retweets. + * + * @param int $post_id + * @param str $network + * @return int number of updated rows (1 if successful, 0 if not) + */ + private function incrementRepostCountCache($post_id, $network) { + return $this->incrementCacheCount ( $post_id, $network, "old_retweet" ); + } + + /** + * Increment retweet cache count, for new-style/native retweets. + * + * @param int $post_id + * @param str $network + * @return int number of updated rows (1 if successful, 0 if not) + */ + private function incrementNativeRTCountCache($post_id, $network) { + return $this->incrementCacheCount ( $post_id, $network, "native_retweet" ); + } + + /** + * Increment either reply_count_cache, old_retweet_count_cache, retweet_count_cache, or favlike_count_cache + * + * @param int $post_id + * @param str $network + * @param str $fieldname + * either "reply" , "old_retweet" (old-style retweets), "native_retweet" or "favlike" + * @return int Number of updated rows + */ + private function incrementCacheCount($post_id, $network, $post_type) { + $fieldname = null; + if ($post_type == "reply") { + $fieldname = "reply"; + } elseif ($post_type == "native_retweet") { + $fieldname = "retweet"; + } elseif ($post_type == "old_retweet") { + $fieldname = "old_retweet"; + } else if ($post_type == "favlike") { + $fieldname = "favlike"; + } + if ($fieldname) { + $q = "UPDATE #prefix#posts SET " . $fieldname . "_count_cache = " . $fieldname . "_count_cache + 1 "; + $q .= "WHERE post_id = :post_id AND network=:network"; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } else { + // log error -- unknown field name + $this->logger->logError ( "unknown field name in incrementCacheCount with $post_id, $post_type", __METHOD__ . ',' . __LINE__ ); + return null; + } + } + + /** + * Checks to see if the $vals array contains all the required fields to insert a post + * + * @param array $vals + * @return bool + */ + private function hasAllRequiredFields($vals) { + $result = true; + foreach ( $this->REQUIRED_FIELDS as $field ) { + if (! isset ( $vals [$field] )) { + $this->logger->logError ( "Missing post $field value", __METHOD__ . ',' . __LINE__ ); + $result = false; + } + } + return $result; + } + public function addPostAndAssociatedInfo(array $vals, $entities = null, $user_array = null) { + $urls = null; + // first add post + $new_post_key = $this->addPost ( $vals ); + // if post did not already exist + if ($new_post_key) { + if ($user_array) { + $u = new User ( $user_array ); + $user_dao = DAOFactory::getDAO ( 'UserDAO' ); + $user_dao->setLoggerInstance ( $this->logger ); + $user_dao->updateUser ( $u ); + } + + if ($entities && isset ( $entities ['urls'] )) { + $urls = $entities ['urls']; + } + URLProcessor::processPostURLs ( $vals ['post_text'], $vals ['post_id'], 'twitter', $this->logger, $urls ); + + if (isset ( $entities )) { + if (isset ( $entities ['mentions'] )) { + $mention_dao = DAOFactory::getDAO ( 'MentionDAO' ); + $mention_dao->setLoggerInstance ( $this->logger ); + $mention_dao->insertMentions ( $entities ['mentions'], $vals ['post_id'], $vals ['author_user_id'], $vals ['network'] ); + } + if (isset ( $entities ['hashtags'] )) { + $hashtagpost_dao = DAOFactory::getDAO ( 'HashtagPostDAO' ); + $hashtagpost_dao->setLoggerInstance ( $this->logger ); + $hashtagpost_dao->insertHashtagPosts ( $entities ['hashtags'], $vals ['post_id'], $vals ['network'] ); + } + + if (isset ( $entities ['place'] )) { + $place = $entities ['place']; + if ($place) { + $place_dao = DAOFactory::getDAO ( 'PlaceDAO' ); + $place_dao->setLoggerInstance ( $this->logger ); + $place_dao->insertPlace ( $place, $vals ['post_id'], $vals ['network'] ); + } + } + } + } + return $new_post_key; + } + public function addPost($vals) { + $vals ['post_id'] = ( string ) $vals ['post_id']; + $retweeted_post_data = null; + // first, check to see if this is a retweet, with the original post available. + if (isset ( $vals ['retweeted_post'] )) { + // $this->logger->logDebug("this is a retweet- first processing original.", __METHOD__.','.__LINE__); + $retweeted_post_data = $vals ['retweeted_post'] ['content']; + // it turns out that for a native retweet, Twitter may not reliably update the count stored in the + // original-- there may be a lag. So, if there is a first retweet, the original post still + // may show 0 rts. This is less common w/ the REST API than the streaming API, but does not hurt to + // address it anyway. So, if we know there was a retweet, but the rt count is showing 0, set it to 1. + // We know it is at least 1. + if (isset ( $retweeted_post_data ['retweet_count_api'] ) && ($retweeted_post_data ['retweet_count_api'] == 0)) { + $retweeted_post_data ['retweet_count_api'] = 1; + } + // since this was a retweet, process the original post first. + if (isset ( $vals ['retweeted_post'] ['entities'] )) { // REST API won't set this + $retweeted_post_entities = $vals ['retweeted_post'] ['entities']; + } else { + $retweeted_post_entities = null; + } + if (isset ( $vals ['retweeted_post'] ['user_array'] )) { // REST API won't set this + $retweeted_post_user_array = $vals ['retweeted_post'] ['user_array']; + } else { + $retweeted_post_user_array = null; + } + $this->addPostAndAssociatedInfo ( $retweeted_post_data, $retweeted_post_entities, $retweeted_post_user_array ); + } + + if ($this->hasAllRequiredFields ( $vals )) { + // process location information + if (! isset ( $vals ['location'] ) && ! isset ( $vals ['geo'] ) && ! isset ( $vals ['place'] )) { + $vals ['is_geo_encoded'] = 6; + } + // process reply + if (isset ( $vals ['in_reply_to_post_id'] ) && $vals ['in_reply_to_post_id'] != '') { + $vals ['in_reply_to_post_id'] = ( string ) $vals ['in_reply_to_post_id']; + $replied_to_post = $this->getPost ( $vals ['in_reply_to_post_id'], $vals ['network'] ); + if (isset ( $replied_to_post )) { + $this->logger->logInfo ( "Found reply.", __METHOD__ . ',' . __LINE__ ); + // check if reply author is followed by the original post author + $follow_dao = DAOFactory::getDAO ( 'FollowDAO' ); + if ($follow_dao->followExists ( $vals ['author_user_id'], $replied_to_post->author_user_id, $replied_to_post->network )) { + $vals ['is_reply_by_friend'] = 1; + $this->logger->logInfo ( "Found reply by a friend!", __METHOD__ . ',' . __LINE__ ); + } + } + } + // process retweet + if (isset ( $vals ['in_retweet_of_post_id'] ) && $vals ['in_retweet_of_post_id'] != '') { + $vals ['in_retweet_of_post_id'] = ( string ) $vals ['in_retweet_of_post_id']; + if (isset ( $retweeted_post_data )) { + // don't need database retrieval to get the necessary data-- use attached orig post info + $retweeted_post = $retweeted_post_data; + $orig_author_id = $retweeted_post_data ['author_user_id']; + $orig_network = $retweeted_post_data ['network']; + } else { + // do database query + $retweeted_post = $this->getPost ( $vals ['in_retweet_of_post_id'], $vals ['network'] ); + if (isset ( $retweeted_post )) { + $orig_author_id = $retweeted_post->author_user_id; + $orig_network = $retweeted_post->network; + } + } + if (isset ( $orig_author_id ) && isset ( $orig_network )) { + $follow_dao = DAOFactory::getDAO ( 'FollowDAO' ); + if ($follow_dao->followExists ( $vals ['author_user_id'], $orig_author_id, $orig_network )) { + $vals ['is_retweet_by_friend'] = 1; + $this->logger->logInfo ( "Found retweet by a friend!", __METHOD__ . ',' . __LINE__ ); + } + } + } + // SQL variables to bind + $vars = array (); + // SQL query + $q = "INSERT IGNORE INTO #prefix#posts SET "; + // Set up required fields + foreach ( $this->REQUIRED_FIELDS as $field ) { + $q .= $field . "=:" . $field . ", "; + $vars [':' . $field] = $vals [$field]; + } + // Set up any optional fields + foreach ( $this->OPTIONAL_FIELDS as $field ) { + if (isset ( $vals [$field] ) && $vals [$field] != '') { + $q .= "" . $field . "=:" . $field . ", "; + $vars [':' . $field] = $vals [$field]; + } + } + // Trim off that last comma and space + $q = substr ( $q, 0, (strlen ( $q ) - 2) ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $res = $this->getInsertId ( $ps ); + + if (isset ( $vals ['retweet_count_api'] ) && ($vals ['retweet_count_api'] > 0) && ! $res) { + // then the post already existed in database & has RT count > 0, so just update the retweet count. + $this->updateAPIRetweetCount ( $vals ['post_id'], $vals ['retweet_count_api'], $vals ['network'] ); + } + if (isset ( $vals ['favlike_count_cache'] ) && ($vals ['favlike_count_cache'] > 0) && ! $res) { + // then the post already existed in database & has fav count > 0, so just update the fav count. + $this->updateFavLikeCount ( $vals ['post_id'], $vals ['network'], $vals ['favlike_count_cache'] ); + } + // if the post was already in the database, but its 'in_retweet_of_post_id' field was not set in the + // earlier insert, then we need to update the existing post to set that info, then increment the old-style + // retweet cache count for the original post it references as well. This situation can arise if the + // stream processor has already seen and saved the old-style retweet (the stream processor won't have + // the in_retweet_of_post_id information for old-style RTs, so a post may first just be treated as a + // mention, but the crawler may later ID it as a RT.). + if (! $res && ! isset ( $retweeted_post_data ) && isset ( $vals ['in_retweet_of_post_id'] ) && $this->updateInRetweetOfPostID ( $vals ['post_id'], $vals ['in_retweet_of_post_id'], $vals ['network'] )) { + $this->incrementRepostCountCache ( $vals ['in_retweet_of_post_id'], $vals ['network'] ); + $status_message = "Repost of " . $vals ['in_retweet_of_post_id'] . " by " . $vals ["author_username"] . " ID: " . $vals ["post_id"] . "; updating old-style retweet cache count"; + $this->logger->logInfo ( $status_message, __METHOD__ . ',' . __LINE__ ); + } + // otherwise, only update the other post records if insert went through. + // This avoids incorrect counts in case of attempt to insert dup. + if ($res) { + if (isset ( $replied_to_post )) { + $this->incrementReplyCountCache ( $vals ['in_reply_to_post_id'], $vals ['network'] ); + $status_message = "Reply found for " . $vals ['in_reply_to_post_id'] . ", ID: " . $vals ["post_id"] . "; updating reply cache count"; + $this->logger->logInfo ( $status_message, __METHOD__ . ',' . __LINE__ ); + } + if (isset ( $retweeted_post )) { + if (! isset ( $retweeted_post_data )) { // if not a native retweet + $this->incrementRepostCountCache ( $vals ['in_retweet_of_post_id'], $vals ['network'] ); + $status_message = "Repost of " . $vals ['in_retweet_of_post_id'] . " by " . $vals ["author_username"] . " ID: " . $vals ["post_id"] . "; updating old-style retweet cache count"; + $this->logger->logInfo ( $status_message, __METHOD__ . ',' . __LINE__ ); + } else { // native retweet + $count = $this->incrementNativeRTCountCache ( $vals ['in_retweet_of_post_id'], $vals ['network'] ); + $status_message = "Retweet of " . $vals ['in_retweet_of_post_id'] . " by " . $vals ["author_username"] . " ID: " . $vals ["post_id"]; + if ($count > 0) { + $status_message .= "; Updated native retweet cache count sucessfully."; + } else { + $status_message .= "; Attempt to update native retweet cache count failed"; + } + $this->logger->logInfo ( $status_message, __METHOD__ . ',' . __LINE__ ); + } + } + } + return $res; + } else { + $status_message = "Could not insert post ID " . $vals ["post_id"] . ", missing values"; + $this->logger->logError ( $status_message, __METHOD__ . ',' . __LINE__ ); + // doesn't have all req'd values + return false; + } + } + public function getAllPosts($author_id, $network, $count, $page = 1, $include_replies = true, $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { + return $this->getAllPostsByUserID ( $author_id, $network, $count, $order_by, $direction, $include_replies, $page, false, $is_public ); + } + public function getAllPostsIterator($author_id, $network, $count, $include_replies = true, $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { + return $this->getAllPostsByUserID ( $author_id, $network, $count, $order_by, $direction, $include_replies, 1, $iterator = true, $is_public ); + } + + /** + * Iterator wrapper for getPostsByFriends + */ + public function getPostsByFriendsIterator($user_id, $network, $count, $is_public = false) { + return $this->getPostsByFriends ( $user_id, $network, $count, 1, $is_public, true ); + } + public function getPostsByFriends($user_id, $network, $count = 15, $page = 1, $is_public = false, $iterator = false) { + $start_on_record = ($page - 1) * $count; + if ($is_public) { + $protected = 'AND p.is_protected = 0 '; + } else { + $protected = ''; + } + + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour AS adj_pub_date "; + $q .= "FROM #prefix#posts AS p "; + $q .= "WHERE p.network = :network "; + $q .= $protected; + $q .= "AND p.author_user_id IN ( "; + $q .= " SELECT user_id FROM #prefix#follows AS f "; + $q .= " WHERE f.follower_id=:user_id AND f.active=1 AND f.network=:network "; + $q .= ")"; + $q .= "ORDER BY p.id DESC "; + $q .= "LIMIT :start_on_record, :limit"; + $vars = array ( + ':user_id' => ( string ) $user_id, + ':network' => $network, + ':limit' => $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + foreach ( $all_rows as $row ) { + $posts [] = new Post ( $row ); + } + return $posts; + } + public function getPostsToUserIterator($user_id, $network, $count, $is_public = false) { + return $this->getPostsToUser ( $user_id, $network, $count, 1, $is_public, true ); + } + public function getPostsToUser($user_id, $network, $count = 15, $page = 1, $is_public = false, $iterator = false) { + $start_on_record = ($page - 1) * $count; + if ($is_public) { + $protected = 'AND p.is_protected = 0 '; + } else { + $protected = ''; + } + + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour AS adj_pub_date "; + $q .= "FROM #prefix#posts AS p "; + $q .= "WHERE p.network = :network "; + $q .= $protected; + $q .= "AND in_reply_to_post_id IS NULL "; + $q .= "AND p.in_reply_to_user_id = :user_id "; + $q .= "ORDER BY p.id DESC "; + $q .= "LIMIT :start_on_record, :limit"; + $vars = array ( + ':user_id' => ( string ) $user_id, + ':network' => $network, + ':limit' => $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllQuestionPosts($author_id, $network, $count, $page = 1, $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { + $start_on_record = ($page - 1) * $count; + + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; + + if ($is_public) { + $protected = 'AND p.is_protected = 0'; + } else { + $protected = ''; + } + + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date FROM ( SELECT * "; + $q .= "FROM #prefix#posts p "; + $q .= "WHERE p.author_user_id = :author_id AND p.network=:network "; + $q .= "AND (in_reply_to_post_id IS null OR in_reply_to_post_id = 0) $protected) AS p "; + $q .= "WHERE post_text RLIKE :format1 OR post_text like :format2 "; + $q .= "ORDER BY " . $order_by . ' ' . $direction . ' '; + $q .= "LIMIT :start_on_record, :limit"; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network, + ':format1' => "\\?$", + ':format2' => "%\\? %", + ':limit' => ( int ) $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllQuestionPostsInRange($author_id, $network, $count, $from, $until, $page = 1, $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; + + if ($is_public) { + $protected = 'AND p.is_protected = 0'; + } else { + $protected = ''; + } + + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date FROM ( SELECT * "; + $q .= "FROM #prefix#posts p "; + $q .= "WHERE p.author_user_id = :author_id AND p.network=:network AND pub_date BETWEEN :from AND :until "; + $q .= "AND (in_reply_to_post_id IS null OR in_reply_to_post_id = 0) $protected) AS p "; + $q .= "WHERE post_text RLIKE :format1 OR post_text like :format2 "; + $q .= "ORDER BY " . $order_by . ' ' . $direction . ' '; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network, + ':format1' => "\\?$", + ':format2' => "%\\? %", + ':from' => $from, + ':until' => $until + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + + /** + * Get all posts by a given user with configurable order by field and direction + * + * @param int $author_id + * @param str $network + * @param int $count + * @param str $order_by + * field name + * @param str $direction + * either "DESC" or "ASC + * @param bool $include_replies + * If true, return posts with in_reply_to_post_id set, if not don't + * @param int $page + * Page number, defaults to 1 + * @param bool $iterator + * Specify whether or not you want a post iterator returned. Default to + * false. + * @param bool $is_public + * Whether or not these results are going to be displayed publicly. Defaults to false. + * @return array Posts with link object set + */ + private function getAllPostsByUserID($author_id, $network, $count, $order_by = "pub_date", $direction = "DESC", $include_replies = true, $page = 1, $iterator = false, $is_public = false) { + $direction = $direction == "DESC" ? "DESC" : "ASC"; + $start_on_record = ($page - 1) * $count; + $order_by = $this->sanitizeOrderBy ( $order_by ); + + // if the 'order_by' string is 'retweets', add an add'l aggregate (sum of two fields) var to the select, + // which we can then sort on. + if ($order_by == 'retweets') { + $q = "SELECT p.*, (p.retweet_count_cache + p.old_retweet_count_cache) as retweets, " . "pub_date + interval #gmt_offset# hour as adj_pub_date "; + } else { + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + } + $q .= "FROM #prefix#posts p "; + $q .= "WHERE author_user_id = :author_id AND p.network=:network "; + if (! $include_replies) { + $q .= "AND (in_reply_to_post_id IS null OR in_reply_to_post_id = 0) "; + } + if ($order_by == 'reply_count_cache') { + $q .= "AND reply_count_cache > 0 "; + } + if ($order_by == 'retweets') { + $q .= "AND (p.retweet_count_cache + p.old_retweet_count_cache) > 0 "; + } + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + $q .= "ORDER BY $order_by $direction "; + $vars = array ( + ':author_id' => $author_id, + ':network' => $network + ); + if (isset ( $count ) && $count > 0) { + $q .= "LIMIT :start_on_record, :limit"; + $vars [':limit'] = ( int ) $count; + $vars [':start_on_record'] = ( int ) $start_on_record; + } + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getHotPosts($author_user_id, $network, $count) { + // Get posts + $q = "SELECT p.*, (p.retweet_count_cache + p.old_retweet_count_cache) as retweets, "; + $q .= "pub_date + interval #gmt_offset# hour as adj_pub_date FROM #prefix#posts p "; + $q .= "WHERE p.author_user_id = :author_user_id AND p.network=:network "; + $q .= "AND (p.reply_count_cache + p.favlike_count_cache + p.retweet_count_cache + p.old_retweet_count_cache) "; + $q .= "> 0 "; + $q .= "AND (p.in_reply_to_post_id IS null OR p.in_reply_to_post_id = 0) "; + $q .= "AND (p.in_reply_to_user_id IS null OR p.in_reply_to_user_id = 0) "; + $q .= "ORDER BY p.pub_date DESC LIMIT :limit"; + $vars = array ( + ':author_user_id' => $author_user_id, + ':network' => $network, + ':limit' => $count + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $posts = array (); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getPostsByUserInRange($author_id, $network, $from, $until, $order_by = "pub_date", $direction = "DESC", $iterator = false, $is_public = false) { + $direction = $direction == "DESC" ? "DESC" : "ASC"; + + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "WHERE author_user_id = :author_id AND p.network=:network AND pub_date BETWEEN :from AND :until "; + if ($order_by == 'reply_count_cache') { + $q .= "AND reply_count_cache > 0 "; + } + if ($order_by == 'retweet_count_cache') { + $q .= "AND retweet_count_cache > 0 "; + } + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + $q .= "ORDER BY $order_by $direction "; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network, + ':from' => $from, + ':until' => $until + ); + // echo Utils::mergeSQLVars($q, $vars); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + if ($all_rows) { + $post_keys_array = array (); + foreach ( $all_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + + /** + * Get all posts by a given user with configurable order by field and direction + * + * @param str $author_username + * @param str $network + * Default "twitter" + * @param int|bool $count + * False if no limit (ie, return all rows) + * @param str $order_by + * field name Default "pub_date" + * @return array Posts with link object set + */ + public function getAllPostsByUsernameOrderedBy($author_username, $network = "twitter", $count = 0, $order_by = "pub_date", $in_last_x_days = 0, $iterator = false, $is_public = false, $since = null) { + $order_by = $this->sanitizeOrderBy ( $order_by ); + $vars = array ( + ':author_username' => $author_username, + ':network' => $network + ); + // if the 'order_by' string is 'retweets', add an add'l aggregate (sum of two fields) var to the select, + // which we can then sort on. + if ($order_by == 'retweets') { + $q = "SELECT p.*, GREATEST((p.retweet_count_cache + p.old_retweet_count_cache), retweet_count_api) " . "as retweets, pub_date + interval #gmt_offset# hour as adj_pub_date "; + } else { + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + } + $q .= "FROM #prefix#posts p "; + $q .= "WHERE author_username = :author_username AND p.network = :network "; + + if ($in_last_x_days > 0) { + if ($since == null) { + $q .= "AND pub_date >= DATE_SUB(CURDATE(), INTERVAL :in_last_x_days DAY) "; + } else { + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :in_last_x_days DAY) "; + $vars [':since'] = $since; + } + $vars [':in_last_x_days'] = ( int ) $in_last_x_days; + } + if ($order_by == 'reply_count_cache') { + $q .= "AND reply_count_cache > 0 "; + } + if ($order_by == 'retweets') { + $q .= "AND (p.retweet_count_cache + p.old_retweet_count_cache) > 0 "; + } + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + $q .= "ORDER BY " . $order_by . " DESC "; + if ($count) { + $q .= "LIMIT :limit"; + $vars [':limit'] = ( int ) $count; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $posts = array (); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllPostsByUsernameIterator($username, $network, $count = 0) { + return $this->getAllPostsByUsernameOrderedBy ( $username, $network, $count, null, null, $iterator = true ); + } + public function getAllPostsByUsername($username, $network) { + return $this->getAllPostsByUsernameOrderedBy ( $username, $network, null, null, null, $iterator = false ); + } + public function getMostRepliedToPostsInLastWeek($username, $network, $count, $is_public = false) { + return $this->getAllPostsByUsernameOrderedBy ( $username, $network, $count, 'reply_count_cache', 7, $iterator = false, $is_public ); + } + public function getMostFavedPostsInLastWeek($username, $network, $count, $is_public = false) { + return $this->getAllPostsByUsernameOrderedBy ( $username, $network, $count, 'favlike_count_cache', 7, $iterator = false, $is_public ); + } + public function getMostRetweetedPostsInLastWeek($username, $network, $count, $is_public = false) { + return $this->getAllPostsByUsernameOrderedBy ( $username, $network, $count, 'retweets', 7, $iterator = false, $is_public ); + } + public function getTotalPostsByUser($author_username, $network) { + $q = "SELECT COUNT(*) as total "; + $q .= "FROM #prefix#posts p "; + $q .= "WHERE author_username = :author_username AND network=:network "; + $q .= "ORDER BY pub_date ASC"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowAsArray ( $ps ); + return $result ["total"]; + } + public function getStatusSources($author_id, $network) { + $q = "SELECT source, count(source) as total "; + $q .= "FROM #prefix#posts WHERE "; + $q .= "author_user_id = :author_id AND network=:network "; + $q .= "GROUP BY source ORDER BY total DESC;"; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDataRowsAsArrays ( $ps ); + } + public function getAllMentionsIterator($author_username, $count, $network = "twitter", $page = 1, $public = false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { + return $this->getMentions ( $author_username, $count, $network, $iterator = true, $page, $public, $include_rts, $order_by, $direction ); + } + public function getAllMentions($author_username, $count, $network = "twitter", $page = 1, $public = false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { + return $this->getMentions ( $author_username, $count, $network, $iterator = false, $page, $public, $include_rts, $order_by, $direction ); + } + private function getMentions($author_username, $count, $network, $iterator, $page = 1, $public = false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { + $start_on_record = ($page - 1) * $count; + + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $direction = ($direction == 'DESC') ? 'DESC' : 'ASC'; + + $author_username = '@' . $author_username; + $q = "SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts AS p "; + $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id "; + $q .= "WHERE p.network = :network AND "; + // fulltext search only works for words longer than 4 chars + if (strlen ( $author_username ) > PostMySQLDAO::FULLTEXT_CHAR_MINIMUM) { + $q .= "MATCH (`post_text`) AGAINST(:author_username IN BOOLEAN MODE) "; + } else { + $author_username = '%' . $author_username . '%'; + $q .= "post_text LIKE :author_username "; + } + if ($public) { + $q .= "AND u.is_protected = 0 "; + } + if ($include_rts == false) { + $q .= 'AND p.in_retweet_of_post_id IS NULL '; + } + $q .= "ORDER BY $order_by $direction "; + $q .= "LIMIT :start_on_record, :limit;"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':start_on_record' => ( int ) $start_on_record, + ':limit' => ( int ) $count + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllMentionsInRange($author_username, $count, $network = "twitter", $from, $until, $page = 1, $public = false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { + return $this->getMentionsInRange ( $author_username, $count, $network, $from, $until, $iterator = false, $page, $public, $include_rts, $order_by, $direction ); + } + private function getMentionsInRange($author_username, $count, $network, $from, $until, $iterator, $page = 1, $public = false, $include_rts = true, $order_by = 'pub_date', $direction = 'DESC') { + $start_on_record = ($page - 1) * $count; + + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $direction = ($direction == 'DESC') ? 'DESC' : 'ASC'; + + $author_username = '@' . $author_username; + $q = " SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts AS p "; + $q .= "INNER JOIN #prefix#users AS u ON p.author_user_id = u.user_id "; + $q .= "WHERE p.network = :network AND pub_date BETWEEN :from AND :until "; + + if (strlen ( $author_username ) > PostMySQLDAO::FULLTEXT_CHAR_MINIMUM) { + $q .= "AND MATCH (`post_text`) AGAINST(:author_username IN BOOLEAN MODE) "; + } else { + $author_username = '%' . $author_username . '%'; + $q .= "AND post_text LIKE :author_username "; + } + if ($public) { + $q .= "AND u.is_protected = 0 "; + } + + if ($include_rts == false) { + $q .= 'AND p.in_retweet_of_post_id IS NULL '; + } + $q .= "ORDER BY $order_by $direction "; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':from' => $from, + ':until' => $until + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($iterator) { + return new PostIterator ( $ps ); + } + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllReplies($user_id, $network, $count, $page = 1, $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { + $start_on_record = ($page - 1) * $count; + + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; + + $q = "SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users u ON p.author_user_id = u.user_id "; + $q .= "WHERE in_reply_to_user_id = :user_id AND p.network=:network "; + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + $q .= "ORDER BY $order_by $direction LIMIT :start_on_record, :limit;"; + $vars = array ( + ':user_id' => ( string ) $user_id, + ':network' => $network, + ':start_on_record' => ( int ) $start_on_record, + ':limit' => ( int ) $count + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getAllRepliesInRange($user_id, $network, $count, $from, $until, $page = 1, $order_by = 'pub_date', $direction = 'DESC', $is_public = false) { + $start_on_record = ($page - 1) * $count; + + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $direction = $direction == 'DESC' ? 'DESC' : 'ASC'; + + $q = "SELECT p.*, u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users u ON p.author_user_id = u.user_id "; + $q .= "WHERE in_reply_to_user_id = :user_id AND p.network=:network AND pub_date BETWEEN :from AND :until "; + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + $q .= "ORDER BY " . $order_by . ' ' . $direction . ' '; + $vars = array ( + ':user_id' => ( string ) $user_id, + ':network' => $network, + ':from' => $from, + ':until' => $until + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + $posts = array (); + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function getMostRepliedToPosts($user_id, $network, $count, $page = 1, $is_public = false) { + return $this->getAllPostsByUserID ( $user_id, $network, $count, "reply_count_cache", "DESC", true, $page, $iterator = false, $is_public ); + } + public function getMostFavedPosts($user_id, $network, $count, $page = 1, $is_public = false) { + return $this->getAllPostsByUserID ( $user_id, $network, $count, "favlike_count_cache", "DESC", true, $page, $iterator = false, $is_public ); + } + public function getMostRetweetedPosts($user_id, $network, $count, $page = 1, $is_public = false) { + return $this->getAllPostsByUserID ( $user_id, $network, $count, "retweets", "DESC", true, $page, $iterator = false, $is_public ); + } + public function getOrphanReplies($username, $count, $network = "twitter", $page = 1) { + $start_on_record = ($page - 1) * $count; + + $username = "@" . $username; + $q = "SELECT p.* , u.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users u ON u.user_id = p.author_user_id WHERE "; + // fulltext search only works for words longer than 4 chars + if (strlen ( $username ) > PostMySQLDAO::FULLTEXT_CHAR_MINIMUM) { + $q .= "MATCH (`post_text`) AGAINST(:username IN BOOLEAN MODE) "; + } else { + $username = '%' . $username . '%'; + $q .= "post_text LIKE :username "; + } + $q .= "AND in_reply_to_post_id is null "; + $q .= "AND in_retweet_of_post_id is null "; + $q .= "AND p.network = :network "; + $q .= "ORDER BY pub_date DESC LIMIT :start_on_record, :limit;"; + $vars = array ( + ':username' => $username, + ':network' => $network, + ':limit' => ( int ) $count, + ':start_on_record' => ( int ) $start_on_record + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + $all_posts = array (); + foreach ( $all_rows as $row ) { + $all_posts [] = $this->setPostWithAuthor ( $row ); + } + return $all_posts; + } + + /** + * Decrement a post's reply_count_cache + * + * @param int $post_id + * @param str $network + * @return in count of affected rows + */ + private function decrementReplyCountCache($post_id, $network) { + $q = "UPDATE #prefix#posts SET reply_count_cache = reply_count_cache - 1 "; + $q .= "WHERE post_id = :post_id AND network=:network "; + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + public function getStrayRepliedToPosts($author_id, $network) { + $q = "SELECT in_reply_to_post_id FROM #prefix#posts p "; + $q .= "WHERE p.author_user_id=:author_id AND p.network=:network "; + $q .= "AND p.in_reply_to_post_id NOT IN (select post_id from #prefix#posts) "; + $q .= "AND p.in_reply_to_post_id NOT IN (select post_id from #prefix#post_errors);"; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDataRowsAsArrays ( $ps ); + } + public function getPostsToGeoencode($limit = 5000) { + $q = "SELECT q.post_id, q.location, q.geo, q.place, q.in_reply_to_post_id, q.in_retweet_of_post_id, "; + $q .= "q.is_reply_by_friend, q.is_retweet_by_friend, q.network FROM "; + $q .= "(SELECT * FROM #prefix#posts AS p WHERE "; + $q .= "(p.geo IS NOT null OR p.place IS NOT null OR p.location IS NOT null) "; + $q .= "AND (p.is_geo_encoded='0' OR p.is_geo_encoded='3') "; + $q .= "ORDER BY id DESC LIMIT :limit) AS q ORDER BY q.id "; + $vars = array ( + ':limit' => ( int ) $limit + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + return $all_rows; + } + public function setGeoencodedPost($post_id, $network, $is_geo_encoded = 0, $location = null, $geodata = null, $distance = 0) { + if ($location && $geodata && ($is_geo_encoded >= 1 && $is_geo_encoded <= 5)) { + $q = "UPDATE #prefix#posts p SET p.location = :location, p.geo = :geo, "; + $q .= "p.reply_retweet_distance = :distance, p.is_geo_encoded = :is_geo_encoded "; + $q .= "WHERE p.post_id = :post_id AND p.network=:network"; + $vars = array ( + ':location' => $location, + ':geo' => $geodata, + ':distance' => $distance, + ':is_geo_encoded' => $is_geo_encoded, + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + } else { + $q = "UPDATE #prefix#posts p SET p.is_geo_encoded = :is_geo_encoded "; + $q .= "WHERE p.post_id = :post_id AND p.network=:network"; + $vars = array ( + ':is_geo_encoded' => $is_geo_encoded, + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + if ($this->getUpdateCount ( $ps ) > 0) { + $logstatus = "Geolocation for $network post $post_id IS_GEO_ENCODED: $is_geo_encoded"; + $this->logger->logInfo ( $logstatus, __METHOD__ . ',' . __LINE__ ); + return true; + } else { + $logstatus = "Geolocation for $network post_id=$post_id IS_GEO_ENCODED: $is_geo_encoded not saved"; + $this->logger->logInfo ( $logstatus, __METHOD__ . ',' . __LINE__ ); + return false; + } + } + public function updateAuthorUsername($author_user_id, $network, $author_username) { + $q = "UPDATE #prefix#posts SET author_username = :author_username "; + $q .= "WHERE author_user_id = :author_user_id AND network=:network"; + $vars = array ( + ':author_user_id' => ( string ) $author_user_id, + ':author_username' => $author_username, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + public function deletePost($id) { + $q = "DELETE from #prefix#posts WHERE id = :id LIMIT 1"; + $vars = array ( + ':id' => $id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + + /** + * Extract location specific to city for each post + * + * @param int $location + * Location as stored in the database + * @return str short_location + */ + private function processLocationRows($full_location) { + $location = explode ( ', ', $full_location ); + $length = count ( $location ); + if ($length > 3) { + return $location [$length - 3] . ', ' . $location [$length - 2] . ', ' . $location [$length - 1]; + } else { + return $full_location; + } + } + + /** + * Convert Distance in kilometers to miles + * + * @param int $distance_in_km + * Distance in KM + * @return int $distance_in_miles + */ + private function calculateDistanceInMiles($distance_in_km) { + $distance_in_miles = round ( $distance_in_km / 1.609 ); + return $distance_in_miles; + } + + /** + * Calculate how much each client is used by a user on a specific network + * + * @param int $author_id + * @param string $network + * @return array First element of the returned array is an array of all the clients the user used, ever. + * The second element is an array of the clients used for the last 25 posts. + * Both arrays are sorted by number of use, descending. + */ + public function getClientsUsedByUserOnNetwork($author_id, $network) { + $q = "SELECT COUNT(*) AS num_posts, source "; + $q .= "FROM #prefix#posts "; + $q .= "WHERE author_user_id = :author_id AND network = :network "; + $q .= "GROUP BY source"; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $rows = $this->getDataRowsAsArrays ( $this->execute ( $q, $vars ) ); + $all_time_clients_usage = self::cleanClientsNames ( $rows ); + + $q = "SELECT COUNT(*) AS num_posts, source"; + $q .= " FROM ("; + $q .= " SELECT *"; + $q .= " FROM #prefix#posts "; + $q .= " WHERE author_user_id = :author_id AND network = :network"; + $q .= " ORDER BY pub_date DESC"; + $q .= " LIMIT 25) p "; + $q .= "GROUP BY source"; + $vars = array ( + ':author_id' => ( string ) $author_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $rows = $this->getDataRowsAsArrays ( $this->execute ( $q, $vars ) ); + $latest_clients_usage = self::cleanClientsNames ( $rows ); + + if (count ( $latest_clients_usage ) == 1 && isset ( $latest_clients_usage [''] )) { + // Plugin doesn't support 'source' + $latest_clients_usage = array (); + } + + return array ( + $all_time_clients_usage, + $latest_clients_usage + ); + } + + /** + * Clean up and sort (by number of use, descending) the source (client) information fetched in + * getClientsUsedByUserOnNetwork. + * To clean up the clients names, we remove the HTML link tag. + * + * @param array $rows + * obtained from the database (as array); columns should be 'num_posts' and 'source' + * @return array Clients names as keys, number of uses as values. + */ + protected static function cleanClientsNames($rows) { + $clients = array (); + foreach ( $rows as $row ) { + $client_name = preg_replace ( '@(.+)@i', '\1', $row ['source'] ); + $clients_key = strtolower ( $client_name ); // will merge together strings with different CaSeS + if (! isset ( $clients [$clients_key] )) { + $clients [$clients_key] = array ( + 'name' => $client_name, + 'count' => 0 + ); + } + $clients [$clients_key] ['count'] += $row ['num_posts']; + } + foreach ( $clients as $key => $client ) { + unset ( $clients [$key] ); + $clients [$client ['name']] = $client ['count']; + } + arsort ( $clients ); + return $clients; + } + public function updateFavLikeCount($post_id, $network, $fav_like_count) { + $q = "UPDATE #prefix#posts SET favlike_count_cache=:favlike_count_cache WHERE post_id=:post_id "; + $q .= "AND network=:network;"; + $vars = array ( + ':favlike_count_cache' => $fav_like_count, + ':post_id' => $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + public function updateReplyCount($post_id, $network, $reply_count) { + $q = "UPDATE #prefix#posts SET reply_count_cache=:reply_count_cache WHERE post_id=:post_id "; + $q .= "AND network=:network;"; + $vars = array ( + ':reply_count_cache' => $reply_count, + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + public function updateRetweetCount($post_id, $network, $retweet_count) { + $q = "UPDATE #prefix#posts SET retweet_count_cache=:retweet_count_cache WHERE post_id=:post_id "; + $q .= "AND network=:network;"; + $vars = array ( + ':retweet_count_cache' => $retweet_count, + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + public function updatePostText($post_id, $network, $post_text) { + $q = "UPDATE #prefix#posts SET post_text=:post_text WHERE post_id=:post_id "; + $q .= "AND network=:network;"; + $vars = array ( + ':post_text' => $post_text, + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + public function getOnThisDayFlashbackPosts($author_id, $network, $from_date = null) { + $vars = array ( + ':author' => strval ( $author_id ), + ':network' => $network + ); + if (! isset ( $from_date )) { + $from_date = 'CURRENT_DATE()'; + } else { + $vars [':from_date'] = $from_date; + $from_date = ':from_date'; + } + $q = "SELECT po.*, po.id AS post_key, po.pub_date + interval #gmt_offset# hour as adj_pub_date, "; + $q .= "pl.place_type, pl.name, pl.full_name, pl.country_code, pl.country, pl.longlat, pl.bounding_box, "; + $q .= "pl.icon, pl.map_image FROM #prefix#posts po "; + $q .= "LEFT JOIN #prefix#places pl ON po.place_id = pl.place_id "; + $q .= "WHERE (YEAR(pub_date)!=YEAR(CURRENT_DATE())) "; + $q .= "AND (DAYOFMONTH(pub_date)=DAYOFMONTH($from_date)) AND (MONTH(pub_date)=MONTH($from_date)) AND "; + $q .= "author_user_id=:author AND po.network=:network AND "; + $q .= "in_reply_to_post_id IS null AND in_reply_to_user_id IS NULL AND "; + $q .= "in_retweet_of_post_id IS NULL AND in_rt_of_user_id IS NULL AND "; + // Don't return posts without post text or place to display + $q .= "(post_text != '' || po.place != '') "; + $q .= "ORDER BY pub_date DESC "; + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['post_key']; + } + + $all_posts = array (); + foreach ( $all_post_rows as $row ) { + $post = new Post ( $row ); + // Create a place object incase this post has some associated place data + $post->place_obj = new Place ( $row ); + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ",", $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $all_posts [] = $post; + } + return array_reverse ( $all_posts ); + } + public function getAllCheckins($author_id, $network, $count = 15, $page = 1) { + // Work out which is the first checkin we want to get + $start_on_record = ($page - 1) * $count; + + // Get the checkins from the database for this user + $q = "SELECT po.id AS post_key, po.post_id, po.author_user_id, po.author_username, po.author_fullname, "; + $q .= "po.author_avatar, po.author_follower_count, po.post_text, po.is_protected, po.source, po.location, "; + $q .= "po.place, po.place_id, po.geo, pub_date, po.in_reply_to_user_id,"; + $q .= "po.in_reply_to_post_id, "; + $q .= "po.reply_count_cache, po.is_reply_by_friend, po.in_retweet_of_post_id, po.old_retweet_count_cache, "; + $q .= "po.is_retweet_by_friend, po.reply_retweet_distance, po.network, po.is_geo_encoded, "; + $q .= "po.in_rt_of_user_id, po.retweet_count_cache, po.retweet_count_api, po.favlike_count_cache, "; + $q .= "po.pub_date + interval #gmt_offset# hour as adj_pub_date, pl.place_id, pl.place_type, pl.name, "; + $q .= "pl.full_name, pl.country_code, pl.country, pl.network, pl.longlat, pl.bounding_box, pl.icon, "; + $q .= "pl.map_image, pl.id "; + $q .= "FROM #prefix#posts po "; + $q .= "JOIN #prefix#places pl ON po.place_id = pl.place_id "; + $q .= "WHERE author_user_id=:author AND po.network=:network AND po.in_reply_to_post_id IS null "; + $q .= "ORDER BY pub_date DESC "; + $vars = array ( + ':author' => $author_id, + ':network' => $network + ); + if (isset ( $count ) && $count > 0) { + $q .= "LIMIT :start_on_record, :limit"; + $vars [':limit'] = ( int ) $count; + $vars [':start_on_record'] = ( int ) $start_on_record; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + + // Get all the post ids of the checkins (we use this later for our link query) + $post_keys_array = array (); + foreach ( $all_rows as $row ) { + $post_keys_array [] = $row ['post_key']; + } + + // An array to store each post object in for the checkins + $all_posts = array (); + foreach ( $all_rows as $row ) { + $data = new Post ( $row ); + // Create a place object for any place related data + $data->place_obj = new Place ( $row ); + // Query for all the links related to these posts / checkins + $q2 = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ",", $post_keys_array ) . ")"; + $ps2 = $this->execute ( $q2 ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps2 ); + + // For each link returned if it equals the post id of this post add the link to this post + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $data->id) { + $data->addLink ( new Link ( $link_row ) ); + } + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + // Now we have all the information for this post store it in our array of all posts + $all_posts [] = $data; + } + return $all_posts; + } + public function countCheckinsToPlaceTypes($author_id, $network) { + $q = "SELECT place_type, COUNT(place_type) AS place_count FROM #prefix#places WHERE place_id IN "; + $q .= "(SELECT place_id FROM #prefix#posts WHERE author_user_id=:author AND network=:network) "; + $q .= "GROUP BY place_type"; + $vars = array ( + 'author' => $author_id, + 'network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + // Convert the results into Google Charts Format + foreach ( $all_rows as $row ) { + /* + * Data needs to be in format {c:[{v: 'Title'}, {v: Value of data for this title}]} e.g. [{c:[{v: 'Park'}, {v: 5}]} + */ + $resultset [] = array ( + 'c' => array ( + array ( + 'v' => $row ['place_type'], + 'f' => $row ['place_type'] + ), + array ( + 'v' => intval ( $row ['place_count'] ) + ) + ) + ); + } + + // Set the meta values like titles etc. + $metadata = array ( + array ( + 'type' => 'string', + 'label' => 'Place Type' + ), + array ( + 'type' => 'number', + 'label' => 'Number of Checkins to this place type' + ) + ); + + // Now encode this data as JSON for the Google Charts API + $visdata = json_encode ( array ( + 'rows' => $resultset, + 'cols' => $metadata + ) ); + + if (count ( $all_rows ) > 0) { + return $visdata; + } else { + return ""; + } + } + public function countCheckinsToPlaceTypesLastWeek($author_id, $network) { + $q = "SELECT place_type, COUNT(place_type) AS place_count FROM #prefix#places WHERE place_id IN "; + $q .= "(SELECT place_id FROM #prefix#posts WHERE author_user_id=:author_id AND network=:network_name "; + $q .= "AND YEARWEEK(pub_date) = YEARWEEK(CURRENT_DATE) AND in_reply_to_post_id IS null ) "; + $q .= "GROUP BY place_type"; + $vars = array ( + ':author_id' => $author_id, + ':network_name' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + + // Convert the results into Google Charts Format + foreach ( $all_rows as $row ) { + /* + * Data needs to be in format {c:[{v: 'Title'}, {v: Value of data for this title}]} e.g. [{c:[{v: 'Park'}, {v: 5}]} + */ + $resultset [] = array ( + 'c' => array ( + array ( + 'v' => $row ['place_type'], + 'f' => $row ['place_type'] + ), + array ( + 'v' => intval ( $row ['place_count'] ) + ) + ) + ); + } + + // Set the meta values like titles etc. + $metadata = array ( + array ( + 'type' => 'string', + 'label' => 'Place Type' + ), + array ( + 'type' => 'number', + 'label' => 'Number of Checkins to this place type' + ) + ); + + // Now encode this data as JSON for the Google Charts API + $visdata = json_encode ( array ( + 'rows' => $resultset, + 'cols' => $metadata + ) ); + + /* + * As we are getting posts from the last week, we might not have any so return a blank string in that instance So we know not to draw this graph on the dashboard. + */ + + if (count ( $all_rows ) > 0) { + return $visdata; + } else { + return ""; + } + } + public function getPostsPerHourDataVis($author_id, $network) { + $posts_per_hour_all_time = $this->getCheckinsPerHourAllTime ( $author_id, $network ); + $posts_per_hour_last_week = $this->getCheckinsPerHourLastWeek ( $author_id, $network ); + + // Convert the results into Google Charts Format + $i = 0; + while ( $i < 24 ) { // Hour 0 through 23 + /* + * Data needs to be in format {c:[{v: 'Hour'}, {v: Number of checkins at this hour}]} e.g. [{c:[{v: '11'}, {v: 5}]} + */ + foreach ( $posts_per_hour_last_week as $row ) { + if ($row ['hour'] == $i) { + $last_week_value = intval ( $row ['total'] ); + break; + } + $last_week_value = 0; + } + + foreach ( $posts_per_hour_all_time as $row ) { + if ($row ['hour'] == $i) { + $all_time_value = intval ( $row ['total'] ); + break; + } + $all_time_value = 0; + } + + $post_data [] = array ( + 'c' => array ( + array ( + 'v' => $i + ), + array ( + 'v' => $last_week_value + ), + array ( + 'v' => $all_time_value + ) + ) + ); + $i ++; + } + + // Set the meta values like titles etc. + $metadata = array ( + array ( + 'type' => 'string', + 'label' => 'Hour of Day' + ), + array ( + 'type' => 'number', + 'label' => 'Checkins Last Week' + ), + array ( + 'type' => 'number', + 'label' => 'Checkins All Time' + ) + ); + + // Now encode this data as JSON for the Google Charts API + return json_encode ( array ( + 'rows' => $post_data, + 'cols' => $metadata + ) ); + } + private function getCheckinsPerHourAllTime($author_id, $network) { + $q = "SELECT HOUR(pub_date) AS hour, COUNT(HOUR(pub_date)) AS total FROM #prefix#posts "; + $q .= "WHERE author_user_id=:author "; + $q .= "AND network=:network AND in_reply_to_post_id IS null GROUP BY HOUR(pub_date)"; + $vars = array ( + 'author' => $author_id, + 'network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDataRowsAsArrays ( $ps ); + } + private function getCheckinsPerHourLastWeek($author_id, $network) { + $q = "SELECT CAST(HOUR(pub_date) AS UNSIGNED) AS hour, COUNT(HOUR(pub_date)) AS total FROM #prefix#posts "; + $q .= "WHERE author_user_id=:author "; + $q .= "AND network=:network AND YEARWEEK(pub_date) = YEARWEEK(CURRENT_DATE) AND in_reply_to_post_id IS null "; + $q .= "GROUP BY HOUR(pub_date)"; + $vars = array ( + 'author' => $author_id, + 'network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDataRowsAsArrays ( $ps ); + } + public function getAllCheckinsInLastWeekAsGoogleMap($author_id, $network) { + $q = "SELECT geo FROM #prefix#posts WHERE author_user_id=:author_id AND network=:network_name "; + $q .= "AND YEARWEEK(pub_date) = YEARWEEK(CURRENT_DATE) AND in_reply_to_post_id IS null "; + $vars = array ( + ':author_id' => $author_id, + ':network_name' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $all_rows = $this->getDataRowsAsArrays ( $ps ); + + $url = 'http://maps.googleapis.com/maps/api/staticmap?size=708x500&maptype=roadmap&markers='; + $url .= 'color:blue%7C'; + + foreach ( $all_rows as $row ) { + $url .= "|" . $row ['geo']; + } + + $url .= '&sensor=false'; + + if (count ( $all_rows ) > 0) { + return $url; + } else { + return ""; + } + } + public function getMostPopularPostsOfTheYear($author_user_id, $network, $year, $count = 25) { + $vars = array ( + ':network_user_id' => $author_user_id, + ':network' => $network, + ':year' => $year, + ':count' => $count + ); + $q = "SELECT po.*, po.id AS post_key, po.pub_date + interval #gmt_offset# hour as adj_pub_date, "; + $q .= "(greatest(retweet_count_cache, retweet_count_api) + reply_count_cache + favlike_count_cache) "; + $q .= "AS total_responses FROM #prefix#posts po WHERE YEAR(pub_date)=:year "; + $q .= "AND po.author_user_id=:network_user_id AND po.network=:network AND "; + $q .= "in_reply_to_post_id IS null AND in_reply_to_user_id IS NULL AND "; + $q .= "in_retweet_of_post_id IS NULL AND in_rt_of_user_id IS NULL "; + $q .= "ORDER BY total_responses DESC LIMIT :count"; + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + foreach ( $post_rows as $row ) { + $post = new Post ( $row ); + $posts [] = $post; + } + return $posts; + } + public function getAverageRetweetCount($author_username, $network, $last_x_days, $since = null) { + if ($since == null) { + $since = date ( 'Y-m-d' ); + } + + $q = "SELECT round(avg(old_retweet_count_cache + retweet_count_cache)) as average_retweet_count "; + $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND (retweet_count_cache > 0 OR old_retweet_count_cache > 0) "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowAsArray ( $ps ); + if (! isset ( $result ["average_retweet_count"] )) { + $q = "SELECT round(avg(retweet_count_api)) as average_retweet_count "; + $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND retweet_count_api > 0 "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowAsArray ( $ps ); + } + return $result ["average_retweet_count"]; + } + public function getAverageFaveCount($author_username, $network, $last_x_days, $since = null) { + if ($since == null) { + $since = date ( 'Y-m-d' ); + } + + $q = "SELECT round(avg(favlike_count_cache)) as average_favlike_count "; + $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND favlike_count_cache > 0 "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowAsArray ( $ps ); + return $result ["average_favlike_count"]; + } + public function getAverageReplyCount($author_username, $network, $last_x_days, $since = null) { + if ($since == null) { + $since = date ( 'Y-m-d' ); + } + + $q = "SELECT round(avg(reply_count_cache)) as average_reply_count "; + $q .= "FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND reply_count_cache > 0 "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY);"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowAsArray ( $ps ); + return $result ["average_reply_count"]; + } + public function doesUserHavePostsWithRetweetsSinceDate($author_username, $network, $last_x_days, $since = null) { + if ($since == null) { + $since = date ( 'Y-m-d' ); + } + + $q = "SELECT id FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND (retweet_count_cache > 0 OR old_retweet_count_cache > 0 OR retweet_count_api > 0) "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY) LIMIT 1;"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowsAsArrays ( $ps ); + if (sizeof ( $result ) < 1) { + return false; + } else { + return true; + } + } + public function doesUserHavePostsWithFavesSinceDate($author_username, $network, $last_x_days, $since = null) { + if ($since == null) { + $since = date ( 'Y-m-d' ); + } + + $q = "SELECT id FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND (favlike_count_cache > 0) "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY) LIMIT 1;"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowsAsArrays ( $ps ); + if (sizeof ( $result ) < 1) { + return false; + } else { + return true; + } + } + public function doesUserHavePostsWithRepliesSinceDate($author_username, $network, $last_x_days, $since = null) { + if ($since == null) { + $since = date ( 'Y-m-d' ); + } + + $q = "SELECT id FROM #prefix#posts WHERE network=:network and author_username=:author_username "; + $q .= "AND in_reply_to_user_id IS null AND in_reply_to_post_id IS null AND in_retweet_of_post_id is null "; + $q .= "AND (reply_count_cache > 0) "; + $q .= "AND pub_date >= DATE_SUB(:since, INTERVAL :last_x_days DAY) LIMIT 1;"; + $vars = array ( + ':author_username' => $author_username, + ':network' => $network, + ':last_x_days' => ( int ) $last_x_days, + ':since' => $since + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowsAsArrays ( $ps ); + if (sizeof ( $result ) < 1) { + return false; + } else { + return true; + } + } + public function getRetweetsByAuthorsOverFollowerCount($post_id, $network, $follower_count_threshold) { + $q = "SELECT u.* "; + $q .= "FROM #prefix#posts p "; + $q .= "INNER JOIN #prefix#users u on p.author_user_id = u.user_id "; + $q .= "WHERE p.network=:network AND in_retweet_of_post_id=:post_id "; + $q .= "AND p.is_protected = 0 AND u.follower_count > :follower_count_threshold "; + $q .= "ORDER BY u.follower_count DESC, p.id DESC LIMIT 5"; + + $vars = array ( + ':post_id' => ( string ) $post_id, + ':network' => $network, + ':follower_count_threshold' => $follower_count_threshold + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + + return $this->getDataRowsAsObjects ( $ps, 'User' ); + } + public function getDaysAgoSinceUserRepliedToRecipient($user_id, $recipient_id, $network) { + $q = "SELECT TIMESTAMPDIFF(DAY, pub_date, NOW()) AS last_reply_days_ago "; + $q .= "FROM #prefix#posts AS p "; + $q .= "WHERE p.author_user_id=:user_id AND p.network=:network AND p.in_reply_to_user_id=:recipient_id "; + $q .= "ORDER BY p.pub_date DESC LIMIT 1"; + + $vars = array ( + ':user_id' => ( string ) $user_id, + ':recipient_id' => ( string ) $recipient_id, + ':network' => $network + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowsAsArrays ( $ps ); + + return count ( $result ) ? ( int ) $result [0] ['last_reply_days_ago'] : null; + } + public function countAllPostsByUserSinceDaysAgo($author_id, $network, $days_ago = 7) { + $q = "SELECT COUNT(*) AS count FROM #prefix#posts AS p "; + $q .= "WHERE p.author_user_id=:user_id AND p.network=:network "; + $q .= "AND p.pub_date>=DATE_SUB(CURDATE(), INTERVAL :days_ago DAY) "; + $vars = array ( + ':user_id' => $author_id, + ':network' => $network, + ':days_ago' => $days_ago + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $result = $this->getDataRowAsArray ( $ps ); + + return ( int ) $result ['count']; + } + public function searchPostsByUser(array $keywords, $network, $author_username, $page_number = 1, $page_count = 20) { + if (! is_array ( $keywords )) { + return null; + } + $start_on_record = ($page_number - 1) * $page_count; + + $q = "SELECT p.*, pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p WHERE author_username=:author_username AND network = :network "; + $q .= "AND ("; + $counter = 0; + $search_terms = array (); + $unique_keywords = array_unique ( $keywords ); + foreach ( $unique_keywords as $keyword ) { + $term = $keyword; + for($i = 1; $i < count ( array_keys ( $keywords, $keyword ) ); $i ++) { + $term .= '%' . $keyword; + } + $search_terms [] = $term; + $q .= " post_text LIKE :keyword" . $counter . " "; + if ($keyword != end ( $unique_keywords )) { + $q .= "AND"; + } + $counter ++; + } + $q .= ") ORDER BY pub_date DESC "; + if ($page_count > 0) { + $q .= "LIMIT :start_on_record, :limit;"; + } else { + $q .= ';'; + } + + $vars = array ( + ':author_username' => $author_username, + ':network' => $network + ); + $counter = 0; + foreach ( $search_terms as $term ) { + $vars [':keyword' . $counter] = '%' . $term . '%'; + $counter ++; + } + if ($page_count > 0) { + $vars [':limit'] = ( int ) $page_count; + $vars [':start_on_record'] = ( int ) $start_on_record; + } + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $post_rows = $this->getDataRowsAsArrays ( $ps ); + + $posts = array (); + foreach ( $post_rows as $row ) { + $post = new Post ( $row ); + $posts [] = $post; + } + return $posts; + } + + /** + * Get all posts by a given hashtag with configurable order by field and direction + * + * @param int $hashtag_id + * @param str $network + * @param int $count + * @param str $order_by + * field name + * @param str $direction + * either "DESC" or "ASC + * @param int $page + * Page number, defaults to 1 + * @param bool $is_public + * Whether or not these results are going to be displayed publicly. Defaults to false. + * @return array Posts with link object set + */ + public function getAllPostsByHashtagId($hashtag_id, $network, $count, $order_by = "pub_date", $direction = "DESC", $page = 1, $is_public = false) { + $direction = $direction == "DESC" ? "DESC" : "ASC"; + $start_on_record = ($page - 1) * $count; + $order_by = $this->sanitizeOrderBy ( $order_by ); + + $q = "SELECT p.*, p.pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p, #prefix#hashtags_posts hp, #prefix#hashtags h "; + $q .= "WHERE p.post_id= hp.post_id AND hp.hashtag_id = h.id AND h.id = :hashtag_id AND p.network=:network "; + + if ($is_public) { + $q .= 'AND p.is_protected = 0 '; + } + + $q .= "ORDER BY p.$order_by $direction "; + + $vars = array ( + ':hashtag_id' => $hashtag_id, + ':network' => $network + ); + + if (isset ( $count ) && $count > 0) { + $q .= "LIMIT :start_on_record, :limit"; + $vars [':limit'] = ( int ) $count; + $vars [':start_on_record'] = ( int ) $start_on_record; + } + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + + $all_post_rows = $this->getDataRowsAsArrays ( $ps ); + + $posts = array (); + + if ($all_post_rows) { + $post_keys_array = array (); + foreach ( $all_post_rows as $row ) { + $post_keys_array [] = $row ['id']; + } + + // Get links + $q = "SELECT * FROM #prefix#links WHERE post_key in (" . implode ( ',', $post_keys_array ) . ")"; + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q ); + $all_link_rows = $this->getDataRowsAsArrays ( $ps ); + + // Combine posts and links + foreach ( $all_post_rows as $post_row ) { + $post = new Post ( $post_row ); + foreach ( $all_link_rows as $link_row ) { + if ($link_row ['post_key'] == $post->id) { + $post->addLink ( new Link ( $link_row ) ); + } + } + $posts [] = $post; + } + } + return $posts; + } + public function deletePostsByHashtagId($hashtag_id) { + $q = "DELETE t.* FROM #prefix#posts t "; + $q .= "INNER JOIN #prefix#hashtags_posts hp ON t.post_id = hp.post_id "; + $q .= "WHERE hp.hashtag_id=:hashtag_id;"; + $vars = array ( + ':hashtag_id' => $hashtag_id + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getDeleteCount ( $ps ); + } + public function searchPostsByHashtag($keywords, Hashtag $hashtag, $network, $page_number = 1, $page_count = 20) { + $start_on_record = ($page_number - 1) * $page_count; + $q = "SELECT p.*, p.pub_date + interval #gmt_offset# hour as adj_pub_date "; + $q .= "FROM #prefix#posts p, #prefix#hashtags_posts hp, #prefix#hashtags h "; + $q .= "WHERE p.post_id= hp.post_id AND hp.hashtag_id = h.id AND p.network=:network AND h.id = :hashtag_id "; + if (sizeof ( $keywords ) > 0) { + $q .= "AND ("; + $counter = 0; + foreach ( $keywords as $keyword ) { + $q .= " post_text LIKE :keyword" . $counter . " "; + if ($keyword != end ( $keywords )) { + $q .= "AND"; + } + $counter ++; + } + $q .= ") "; + } + $q .= "ORDER BY pub_date DESC "; + if ($page_count > 0) { + $q .= "LIMIT :start_on_record, :limit;"; + } else { + $q .= ';'; + } + $vars = array ( + ':network' => $network, + ':hashtag_id' => $hashtag->id + ); + if (sizeof ( $keywords ) > 0) { + $counter = 0; + foreach ( $keywords as $keyword ) { + $vars [':keyword' . $counter] = '%' . $keyword . '%'; + $counter ++; + } + } + if ($page_count > 0) { + $vars [':limit'] = ( int ) $page_count; + $vars [':start_on_record'] = ( int ) $start_on_record; + } + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $post_rows = $this->getDataRowsAsArrays ( $ps ); + + $posts = array (); + foreach ( $post_rows as $row ) { + $post = new Post ( $row ); + $posts [] = $post; + } + return $posts; + } + + public function getMostFavCommentPostsByUserId($author_id, $network) { + $q = "SELECT *, pub_date + interval #gmt_offset# hour AS adj_pub_date FROM #prefix#posts "; + $q .= "WHERE author_user_id = :author_user_id AND network = :network "; + $q .= "AND DATE(pub_date + interval #gmt_offset# hour) = CURRENT_DATE() "; + $q .= "AND reply_count_cache + favlike_count_cache = "; + $q .= "(SELECT MAX(reply_count_cache + favlike_count_cache) "; + $q .= "FROM #prefix#posts WHERE author_user_id = :author_user_id AND network = :network "; + $q .= "AND DATE(pub_date + interval #gmt_offset# hour) = CURRENT_DATE() "; + $q .= "AND reply_count_cache + favlike_count_cache <> 0) "; + + $vars = array ( + ':author_user_id' => $author_id, + ':network' => $network + ); + + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + foreach ( $post_rows as $row ) { + $post = new Post ( $row ); + $posts [] = $post; + } + return $posts; + } + + public function updateSharesCount($post_id, $network, $shares_count) { + $q = "UPDATE #prefix#posts SET shares_count_cache=:shares_count WHERE post_id=:post_id "; + $q .= "AND network=:network;"; + $vars = array ( + ':shares_count' => $shares_count, + ':post_id' => ( string ) $post_id, + ':network' => $network + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + return $this->getUpdateCount ( $ps ); + } + + public function getMostSharedPostsOfTheLastDays($author_id, $network, $days) { + $q = "SELECT *, pub_date + interval #gmt_offset# hour AS adj_pub_date "; + $q .= "FROM #prefix#posts WHERE author_user_id = :author_user_id AND network = :network "; + $q .= "AND (pub_date + interval #gmt_offset# hour) >= DATE_SUB(CURRENT_DATE(), INTERVAL :days DAY)"; + $q .= "AND shares_count_cache > 0 ORDER BY shares_count_cache DESC LIMIT 3"; + $vars = array ( + ':author_user_id' => $author_id, + ':network' => $network, + ':days' => $days + ); + if ($this->profiler_enabled) { + Profiler::setDAOMethod ( __METHOD__ ); + } + $ps = $this->execute ( $q, $vars ); + $post_rows = $this->getDataRowsAsArrays ( $ps ); + $posts = array (); + foreach ( $post_rows as $row ) { + $post = new Post ( $row ); + $posts [] = $post; + } + return $posts; + } } diff --git a/webapp/_lib/dao/class.UserMySQLDAO.php b/webapp/_lib/dao/class.UserMySQLDAO.php index d2ff24d15c..7675bac82f 100644 --- a/webapp/_lib/dao/class.UserMySQLDAO.php +++ b/webapp/_lib/dao/class.UserMySQLDAO.php @@ -98,6 +98,8 @@ public function updateUser($user) { ':username'=>$user->username, ':full_name'=>$user->full_name, ':avatar'=>$user->avatar, + ':gender'=>$user->gender, + ':birthday'=>$user->birthday, ':location'=>$user->location, ':description'=>$user->description, ':url'=>$user->url, @@ -113,19 +115,22 @@ public function updateUser($user) { $is_user_in_storage = false; $is_user_in_storage = $this->isUserInDB($user->user_id, $user->network); if (!$is_user_in_storage) { - $q = "INSERT INTO #prefix#users (user_id, user_name, full_name, avatar, location, description, url, "; + $q = "INSERT INTO #prefix#users (user_id, user_name, full_name, avatar, gender, birthday, location, "; + $q .= "description, url, "; $q .= "is_verified, is_protected, follower_count, post_count, ". ($has_friend_count ? "friend_count, " : "")." ". ($has_favorites_count ? "favorites_count, " : "")." ". ($has_last_post ? "last_post, " : "")." found_in, joined, network ". ($has_last_post_id ? ", last_post_id" : "").") "; - $q .= "VALUES ( :user_id, :username, :full_name, :avatar, :location, :description, :url, :is_verified, "; + $q .= "VALUES ( :user_id, :username, :full_name, :avatar, :gender, :birthday, :location, :description, "; + $q .= ":url, :is_verified, "; $q .= ":is_protected, :follower_count, :post_count, ".($has_friend_count ? ":friend_count, " : "")." ". ($has_favorites_count ? ":favorites_count, " : "")." ". ($has_last_post ? ":last_post, " : "")." :found_in, :joined, :network ". ($has_last_post_id ? ", :last_post_id " : "")." )"; } else { - $q = "UPDATE #prefix#users SET full_name = :full_name, avatar = :avatar, location = :location, "; + $q = "UPDATE #prefix#users SET full_name = :full_name, avatar = :avatar, gender = :gender, "; + $q .= "birthday = :birthday, location = :location, "; $q .= "user_name = :username, description = :description, url = :url, is_verified = :is_verified, "; $q .= "is_protected = :is_protected, follower_count = :follower_count, post_count = :post_count, ". ($has_friend_count ? "friend_count= :friend_count, " : "")." ". diff --git a/webapp/_lib/dao/interface.FavoritePostDAO.php b/webapp/_lib/dao/interface.FavoritePostDAO.php index 314f443b84..3d61500b05 100755 --- a/webapp/_lib/dao/interface.FavoritePostDAO.php +++ b/webapp/_lib/dao/interface.FavoritePostDAO.php @@ -128,4 +128,44 @@ public function getFavoritesFromOneYearAgo($fav_of_user_id, $network, $from_date * @return array User objects */ public function getUsersWhoFavoritedMostOfYourPosts($author_user_id, $network, $last_x_days); + + /** + * Get gender of users who favorited post. + * @param $post_id + * @return array of count for male and female + */ + public function getGenderOfFavoriters($post_id); + + /** + * Get gender of users who commented post. + * @param $author_user_id + * @param $network + * @param $last_x_days + * @return array of count for male and female + */ + public function getGenderOfCommenters($post_id); + /** + * Get bithday of users who favorited post. + * @param $post_id + * @return array with favoriter's birthdays + */ + public function getBirthdayOfFavoriters($post_id); + /** + * Get bithday of users who commented post. + * @param $post_id + * @return array with commenter's birthdays + */ + public function getBirthdayOfCommenters($post_id); + /** + * Get location of users who favorited post. + * @param $post_id + * @return array with favoriter's location + */ + public function getLocationOfFavoriters($post_id); + /** + * Get location of users who commented post. + * @param $post_id + * @return array with commenter's location + */ + public function getLocationOfCommenters($post_id); } \ No newline at end of file diff --git a/webapp/_lib/dao/interface.PostDAO.php b/webapp/_lib/dao/interface.PostDAO.php index 4229ca726d..99c5a2ac23 100644 --- a/webapp/_lib/dao/interface.PostDAO.php +++ b/webapp/_lib/dao/interface.PostDAO.php @@ -789,4 +789,30 @@ public function deletePostsByHashtagId($hashtag_id); * @return arr of Post objects */ public function searchPostsByHashtag($keywords, Hashtag $hashtag, $network, $page_number=1, $page_count=20); + + /** + * Get today's post with the biggest sum of likes and comments. + * @param int $author_id + * @param str $network + * @return Post object + */ + public function getMostFavCommentPostsByUserId($author_id, $network); + + /** + * Update the shares count cache for a post. + * @param str $post_id + * @param str $network + * @param str $shares_count + * @return int Number of updated rows + */ + public function updateSharesCount($post_id, $network, $shares_count); + + /** + * Get the top 3 posts with the most shares of last days. + * @param int $author_id + * @param str $network + * @param int $days + * @return Post object + */ +public function getMostSharedPostsOfTheLastDays($author_id, $network, $days); } diff --git a/webapp/_lib/model/class.Post.php b/webapp/_lib/model/class.Post.php index 7287ab0608..1c25dbadc3 100644 --- a/webapp/_lib/model/class.Post.php +++ b/webapp/_lib/model/class.Post.php @@ -154,6 +154,10 @@ class Post { * @var int The total number of favorites or likes this post received. */ var $favlike_count_cache; + /** + * @var int The total number of shares this post received. + */ + var $shares_count_cache; /** * * @var User $author Optionally set @@ -220,6 +224,7 @@ public function __construct($val) { $this->is_reply_by_friend = PDODAO::convertDBToBool($val["is_reply_by_friend"]); $this->is_retweet_by_friend = PDODAO::convertDBToBool($val["is_retweet_by_friend"]); $this->favlike_count_cache = $val["favlike_count_cache"]; + $this->shares_count_cache = $val["shares_count_cache"]; // favorited is non-persistent. Will be set from xml, but not from database retrieval. if (isset($val["favorited"])) { @@ -267,7 +272,7 @@ public static function extractURLs($post_text) { $url_pattern = '(?i)\b'. '((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)'. '(?:[^\s()<>/][^\s()<>]*|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+'. - '(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“â€â€˜â€™,Ó]))'; + '(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“â€â€˜â€™,�]))'; preg_match_all('#'.$url_pattern.'#', $post_text, $matches); $corrected_urls = array_map( 'Link::addMissingHttp', $matches[0]); return array_filter($corrected_urls,'Utils::validateURL'); diff --git a/webapp/_lib/model/class.User.php b/webapp/_lib/model/class.User.php index 558992cf83..8c13d31b83 100644 --- a/webapp/_lib/model/class.User.php +++ b/webapp/_lib/model/class.User.php @@ -54,6 +54,16 @@ class User { * @var str */ var $avatar; + /** + * + * @var str + */ + var $gender; + /** + * + * @var str + */ + var $birthday; /** * * @var location @@ -156,6 +166,12 @@ public function __construct($val = false, $found_in = false) { $this->full_name = $val['full_name']; $this->user_id = $val['user_id']; $this->avatar = $val['avatar']; + if (isset($val['gender'])) { + $this->gender = $val['gender']; + } + if (isset($val['birthday'])) { + $this->birthday = $val['birthday']; + } $this->location = $val['location']; $this->description = $val['description']; $this->url = $val['url']; diff --git a/webapp/install/sql/build-db_mysql-upcoming-release.sql b/webapp/install/sql/build-db_mysql-upcoming-release.sql index 586c580069..e0b0c627a2 100644 --- a/webapp/install/sql/build-db_mysql-upcoming-release.sql +++ b/webapp/install/sql/build-db_mysql-upcoming-release.sql @@ -1,6 +1,6 @@ -- -- ThinkUp Database Creation Script --- Auto-generated by thinkup/extras/scripts/migratedb script on 2014-03-11 +-- Auto-generated by thinkup/extras/scripts/migratedb script on 2014-06-15 -- ALTER DATABASE DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; @@ -549,6 +549,8 @@ CREATE TABLE tu_users ( user_name varchar(255) NOT NULL COMMENT 'Username on a given network, like a user''s Twitter username or Facebook user name.', full_name varchar(255) NOT NULL COMMENT 'Full name on a given network.', avatar varchar(255) NOT NULL COMMENT 'URL to user''s avatar on a given network.', + gender varchar(255) DEFAULT NULL COMMENT 'Gender of user', + birthday varchar(255) DEFAULT NULL COMMENT 'Birthday of user', location varchar(255) DEFAULT NULL COMMENT 'Service user location.', description text COMMENT 'Service user description, like a Twitter user''s profile description.', url varchar(255) DEFAULT NULL COMMENT 'Service user''s URL.', @@ -592,7 +594,7 @@ CREATE TABLE tu_videos ( ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Videos which appear in posts.'; --- Dump completed on 2014-03-11 11:30:55 +-- Dump completed on 2014-06-15 21:58:43 -- -- Insert DB Version diff --git a/webapp/install/sql/build-db_mysql.sql b/webapp/install/sql/build-db_mysql.sql index ec7cc1b773..178c97ce93 100644 --- a/webapp/install/sql/build-db_mysql.sql +++ b/webapp/install/sql/build-db_mysql.sql @@ -462,6 +462,7 @@ CREATE TABLE tu_posts ( retweet_count_cache int(11) NOT NULL DEFAULT '0' COMMENT 'Manual count of native retweets as determined by ThinkUp. [Twitter-specific]', retweet_count_api int(11) NOT NULL DEFAULT '0' COMMENT 'The total number of native retweets as reported by Twitter API. [Twitter-specific]', favlike_count_cache int(11) NOT NULL DEFAULT '0' COMMENT 'The total number of favorites or likes this post received.', + shares_count_cache int(11) NOT NULL DEFAULT '0' COMMENT 'The total number of shares this post received.', permalink text COMMENT 'Link to this post on the respective service.', PRIMARY KEY (id), UNIQUE KEY post_network (post_id,network), @@ -526,6 +527,8 @@ CREATE TABLE tu_users ( user_name varchar(255) NOT NULL COMMENT 'Username on a given network, like a user''s Twitter username or Facebook user name.', full_name varchar(255) NOT NULL COMMENT 'Full name on a given network.', avatar varchar(255) NOT NULL COMMENT 'URL to user''s avatar on a given network.', + gender varchar(255) DEFAULT NULL COMMENT 'Gender of user', + birthday varchar(255) DEFAULT NULL COMMENT 'Birthday of user', location varchar(255) DEFAULT NULL COMMENT 'Service user location.', description text COMMENT 'Service user description, like a Twitter user''s profile description.', url varchar(255) DEFAULT NULL COMMENT 'Service user''s URL.', diff --git a/webapp/install/sql/mysql_migrations/2014-05-29_add-gender-to-users_issue1943.sql b/webapp/install/sql/mysql_migrations/2014-05-29_add-gender-to-users_issue1943.sql new file mode 100644 index 0000000000..80f8802907 --- /dev/null +++ b/webapp/install/sql/mysql_migrations/2014-05-29_add-gender-to-users_issue1943.sql @@ -0,0 +1 @@ +ALTER TABLE tu_users ADD gender VARCHAR ( 255 ) DEFAULT NULL; \ No newline at end of file diff --git a/webapp/install/sql/mysql_migrations/2014-06-15_add-birthday-to-users_issue1944.sql b/webapp/install/sql/mysql_migrations/2014-06-15_add-birthday-to-users_issue1944.sql new file mode 100644 index 0000000000..d480f7d6cb --- /dev/null +++ b/webapp/install/sql/mysql_migrations/2014-06-15_add-birthday-to-users_issue1944.sql @@ -0,0 +1 @@ +ALTER TABLE tu_users ADD birthday datetime DEFAULT NULL; \ No newline at end of file diff --git a/webapp/install/sql/mysql_migrations/2014-07-22_add-shares_count_cache-to-posts_issue1947.sql b/webapp/install/sql/mysql_migrations/2014-07-22_add-shares_count_cache-to-posts_issue1947.sql new file mode 100644 index 0000000000..c687615e09 --- /dev/null +++ b/webapp/install/sql/mysql_migrations/2014-07-22_add-shares_count_cache-to-posts_issue1947.sql @@ -0,0 +1 @@ +ALTER TABLE tu_posts ADD shares_count_cache int(11) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/webapp/plugins/facebook/controller/class.FacebookPluginConfigurationController.php b/webapp/plugins/facebook/controller/class.FacebookPluginConfigurationController.php index 76fca46dc7..c69e16a07b 100644 --- a/webapp/plugins/facebook/controller/class.FacebookPluginConfigurationController.php +++ b/webapp/plugins/facebook/controller/class.FacebookPluginConfigurationController.php @@ -124,8 +124,8 @@ protected function setUpFacebookInteractions($options) { } if ($this->do_show_add_button) { - $params = array('scope'=>'read_stream,user_likes,user_location,user_website,'. - 'read_friendlists,friends_location,manage_pages,read_insights,manage_pages', + $params = array('scope'=>'read_stream,user_birthday,user_likes,user_location,user_website,'. + 'read_friendlists,friends_birthday,friends_location,manage_pages,read_insights,manage_pages', 'state'=>SessionCache::get('facebook_auth_csrf'), 'redirect_uri'=> (Utils::getApplicationURL(). 'account/?p=facebook') ); diff --git a/webapp/plugins/facebook/model/class.FacebookCrawler.php b/webapp/plugins/facebook/model/class.FacebookCrawler.php index dacbc4d9a5..d7f4aaeff7 100644 --- a/webapp/plugins/facebook/model/class.FacebookCrawler.php +++ b/webapp/plugins/facebook/model/class.FacebookCrawler.php @@ -78,9 +78,9 @@ public function fetchUser($user_id, $found_in, $force_reload_from_facebook=false $user_dao = DAOFactory::getDAO('UserDAO'); $user_object = null; if ($force_reload_from_facebook || !$user_dao->isUserInDB($user_id, $network)) { - // Get owner user details and save them to DB - $fields = $network!='facebook page'?'id,name,about,location,website':''; - $user_details = FacebookGraphAPIAccessor::apiRequest('/'.$user_id, $this->access_token, $fields); + // Get owner user details and save them to DB + $fields = $network!='facebook page'?'id,name,gender,birthday,about,location,website':''; + $user_details = FacebookGraphAPIAccessor::apiRequest('/'.$user_id, $this->access_token, $fields ); if (isset($user_details)) { $user_details->network = $network; } @@ -124,6 +124,8 @@ private function parseUserDetails($details) { $user_vals["user_name"] = $details->name; $user_vals["full_name"] = $details->name; $user_vals["user_id"] = $details->id; + $user_vals["gender"] = $details->gender; + $user_vals["birthday"] = $details->birthday; $user_vals["avatar"] = 'https://graph.facebook.com/'.$details->id.'/picture'; $user_vals['url'] = isset($details->website)?$details->website:''; $user_vals["follower_count"] = 0; @@ -225,12 +227,12 @@ private function processStream($stream, $network, $page_number) { $post_dao = DAOFactory::getDAO('PostDAO'); - foreach ($stream->data as $index=>$p) { + foreach ($stream->data as $index=>$p) { $post_id = explode("_", $p->id); $post_id = $post_id[1]; $this->logger->logInfo("Beginning to process ".$post_id.", post ".($index+1)." of ".count($stream->data). " on page ".$page_number, __METHOD__.','.__LINE__); - + // stream can contain posts from multiple users. get profile for this post $profile = null; if (!empty($profiles[$p->from->id])) { @@ -249,7 +251,6 @@ private function processStream($stream, $network, $page_number) { $p->likes = $this->normalizeLikes($p->likes); $likes_count = $p->likes->count; } - // Normalize comments to be one array if (isset($p->comments)) { $p->comments = $this->normalizeComments($p->comments); @@ -298,6 +299,7 @@ private function processStream($stream, $network, $page_number) { "post_text"=>isset($p->message)?$p->message:'', "pub_date"=>$p->created_time, "favlike_count_cache"=>$likes_count, + "shares_count_cache"=>$p->shares->count, // assume only one recipient "in_reply_to_user_id"=> isset($p->to->data[0]->id) ? $p->to->data[0]->id : '', "in_reply_to_post_id"=>'', @@ -334,6 +336,7 @@ private function processStream($stream, $network, $page_number) { //free up memory $thinkup_links = array(); } else { // post already exists in storage + $post_dao->updateSharesCount($post_id, $network, $p->shares->count); if ($must_process_likes) { //update its like count only $post_dao->updateFavLikeCount($post_id, $network, $likes_count); $this->logger->logInfo("Updated Like count for post ".$post_id . " to ". $likes_count, @@ -364,6 +367,8 @@ private function processStream($stream, $network, $page_number) { $comment_to_process = array("post_id"=>$comment_id, "author_username"=>$c->from->name, "author_fullname"=>$c->from->name, + "author_gender"=>$c->from->gender, + "author_birthday"=>$c->from->birthday, "author_avatar"=>'https://graph.facebook.com/'.$c->from->id.'/picture', "author_user_id"=>$c->from->id,"post_text"=>$c->message, "pub_date"=>$c->created_time, "in_reply_to_user_id"=>$profile->user_id, @@ -413,6 +418,8 @@ private function processStream($stream, $network, $page_number) { if (!isset($comment_in_storage)) { $comment_to_process = array("post_id"=>$comment_id, "author_username"=>$c->from->name, "author_fullname"=>$c->from->name, + "author_gender"=>$c->from->gender, + "author_birthday"=>$c->from->birthday, "author_avatar"=>'https://graph.facebook.com/'. $c->from->id.'/picture', "author_user_id"=>$c->from->id, "post_text"=>$c->message, "pub_date"=>$c->created_time, @@ -466,15 +473,16 @@ private function processStream($stream, $network, $page_number) { $post_likes = $p->likes->data; $post_likes_count = isset($post_likes)?sizeof($post_likes):0; if (is_array($post_likes) && sizeof($post_likes) > 0) { - foreach ($post_likes as $l) { + foreach ($post_likes as $l) { if (isset($l->name) && isset($l->id)) { //Get users $user_to_add = array("user_name"=>$l->name, "full_name"=>$l->name, - "user_id"=>$l->id, "avatar"=>'https://graph.facebook.com/'.$l->id. + "user_id"=>$l->id, "gender"=>$l->gender, "birthday"=>$l->birthday, "avatar"=>'https://graph.facebook.com/'.$l->id. '/picture', "location"=>'', "description"=>'', "url"=>'', "is_protected"=>1, "follower_count"=>0, "post_count"=>0, "joined"=>'', "found_in"=>"Likes", "network"=>'facebook'); //Users are always set to network=facebook array_push($thinkup_users, $user_to_add); + $fav_to_add = array("favoriter_id"=>$l->id, "network"=>$network, "author_user_id"=>$profile->user_id, "post_id"=>$post_id); @@ -518,7 +526,7 @@ private function processStream($stream, $network, $page_number) { if (isset($l->name) && isset($l->id)) { //Get users $user_to_add = array("user_name"=>$l->name, "full_name"=>$l->name, - "user_id"=>$l->id, "avatar"=>'https://graph.facebook.com/'.$l->id. + "user_id"=>$l->id, "gender"=>$l->gender, "birthday"=>$l->birthday, "avatar"=>'https://graph.facebook.com/'.$l->id. '/picture', "location"=>'', "description"=>'', "url"=>'', "is_protected"=>1, "follower_count"=>0, "post_count"=>0, "joined"=>'', "found_in"=>"Likes", "network"=>'facebook'); //Users are always set to network=facebook diff --git a/webapp/plugins/facebook/model/class.FacebookGraphAPIAccessor.php b/webapp/plugins/facebook/model/class.FacebookGraphAPIAccessor.php index 3309ce5ffa..9f9534aa7c 100644 --- a/webapp/plugins/facebook/model/class.FacebookGraphAPIAccessor.php +++ b/webapp/plugins/facebook/model/class.FacebookGraphAPIAccessor.php @@ -47,7 +47,7 @@ public static function apiRequest($path, $access_token, $fields=null) { if ($fields != null ) { $url = $url.'&fields='.$fields; } - $result = Utils::getURLContents($url); + $result = Utils::getURLContents($url); return json_decode($result); } /** @@ -61,7 +61,7 @@ public static function apiRequest($path, $access_token, $fields=null) { */ public static function rawApiRequest($path, $decode_json=true) { if ($decode_json) { - $result = Utils::getURLContents($path); + $result = Utils::getURLContents($path); return json_decode($result); } else { return Utils::getURLContents($path); diff --git a/webapp/plugins/facebook/model/class.FacebookPlugin.php b/webapp/plugins/facebook/model/class.FacebookPlugin.php index 9ce75a0b55..9237d042d9 100644 --- a/webapp/plugins/facebook/model/class.FacebookPlugin.php +++ b/webapp/plugins/facebook/model/class.FacebookPlugin.php @@ -81,7 +81,6 @@ public function crawl() { $tokens = $owner_instance_dao->getOAuthTokens($instance->id); $access_token = $tokens['oauth_access_token']; - $instance_dao->updateLastRun($instance->id); $facebook_crawler = new FacebookCrawler($instance, $access_token, $max_crawl_time); $dashboard_module_cacher = new DashboardModuleCacher($instance); diff --git a/webapp/plugins/facebook/tests/TestOfFacebookCrawler.php b/webapp/plugins/facebook/tests/TestOfFacebookCrawler.php index 72c9a29b5a..afd3ff8335 100644 --- a/webapp/plugins/facebook/tests/TestOfFacebookCrawler.php +++ b/webapp/plugins/facebook/tests/TestOfFacebookCrawler.php @@ -136,6 +136,8 @@ public function testFetchUser() { $this->assertEqual($user->username, 'Gina Trapani'); $this->assertEqual($user->full_name, 'Gina Trapani'); $this->assertEqual($user->user_id, 606837591); + $this->assertEqual($user->gender, "female"); + $this->assertEqual($user->birthday, "01/02/03"); $this->assertEqual($user->location, "San Diego, California"); $this->assertEqual($user->description, 'Blogger and software developer. Project Director at Expert Labs. Co-host of This Week in Google.'); @@ -197,6 +199,8 @@ public function testFetchPostsAndRepliesForProfile1() { $this->assertEqual($user->user_id, '606837591'); $this->assertEqual($user->avatar, 'https://graph.facebook.com/606837591/picture'); $this->assertTrue($user->is_protected); + $this->assertEqual($user->gender, 'female'); + $this->assertEqual($user->birthday, "01/02/03"); $this->assertEqual($user->location, 'San Diego, California'); //sleep(1000); $user = $user_dao->getUserByName('Mitch Wagner', 'facebook'); @@ -204,6 +208,8 @@ public function testFetchPostsAndRepliesForProfile1() { $this->assertEqual($user->user_id, '697015835'); $this->assertEqual($user->avatar, 'https://graph.facebook.com/697015835/picture'); $this->assertTrue($user->is_protected); + $this->assertEqual($user->gender, 'male'); + $this->assertEqual($user->birthday, "07/23"); $this->assertEqual($user->location, 'La Mesa, California'); $user = $user_dao->getUserByName('Jeffrey McManus', 'facebook'); @@ -211,6 +217,8 @@ public function testFetchPostsAndRepliesForProfile1() { $this->assertEqual($user->user_id, '691270740'); $this->assertEqual($user->avatar, 'https://graph.facebook.com/691270740/picture'); $this->assertTrue($user->is_protected); + $this->assertEqual($user->gender, 'male'); + $this->assertEqual($user->birthday, "01/02/03"); $this->assertEqual($user->location, ''); } @@ -226,10 +234,9 @@ public function testFetchPostsAndRepliesForProfile2() { $post = $post_dao->getPost('10150328374252744', 'facebook'); $this->assertEqual($post->post_text, ''); $this->assertEqual(sizeof($post->links), 1); - $this->assertEqual($post->links[0]->url, + $this->assertEqual($post->links[0]->url, 'http://www.youtube.com/v/DC1g_Aq3dUc?feature=autoshare&version=3&autohide=1&autoplay=1'); - $this->assertEqual($post->links[0]->expanded_url, - ''); + $this->assertEqual($post->links[0]->expanded_url,''); $this->assertEqual($post->links[0]->caption, 'Liked on www.youtube.com'); $this->assertEqual($post->links[0]->description, 'A fan made trailer for the Warner Bros. production of Superman Returns. Fan trailer produced and edited by '. @@ -283,14 +290,13 @@ public function testFetchPostsAndRepliesForPage() { $post = $post_dao->getPost('437900891355', 'facebook page'); $this->assertEqual($post->post_text, 'Top 10 iOS Jailbreak Hacks'); $this->assertFalse($post->is_protected); - $this->assertEqual($post->reply_count_cache, 45); + $this->assertEqual($post->reply_count_cache, 25); //test link with image $this->assertEqual(sizeof($post->links), 1); $this->assertEqual($post->links[0]->url, 'http://lifehacker.com/5653429/top-10-ios-jailbreak-hacks'); - $this->assertEqual($post->links[0]->expanded_url, - ''); + $this->assertEqual($post->links[0]->expanded_url, ''); $this->assertEqual($post->links[0]->image_src, 'http://platform.ak.fbcdn.net/www/app_full_proxy.php?app=45439413586&v=1&size=z&cksum=7de062ac249fe7caef80f66'. 'f49a38818&src=http%3A%2F%2Fcache-02.gawkerassets.com%2Fassets%2Fimages%2F17%2F2010%2F10%2F160x120_jailbreak-'. @@ -305,7 +311,6 @@ public function testFetchPostsAndRepliesForPage() { $this->assertEqual($user->full_name, 'Matthew Fleisher'); $this->assertEqual($user->network, 'facebook'); $this->assertTrue($user->is_protected); - $user = $ud->getUserByName('Matthew Fleisher', 'facebook page'); $this->assertEqual($user, null); diff --git a/webapp/plugins/facebook/tests/classes/mock.FacebookGraphAPIAccessor.php b/webapp/plugins/facebook/tests/classes/mock.FacebookGraphAPIAccessor.php index e9b0558c7f..8ac9869212 100644 --- a/webapp/plugins/facebook/tests/classes/mock.FacebookGraphAPIAccessor.php +++ b/webapp/plugins/facebook/tests/classes/mock.FacebookGraphAPIAccessor.php @@ -79,12 +79,11 @@ private static function decodeFileContents($file_path, $decode_json=true) { */ public static function rawApiRequest($path, $decode_json=true) { $url = $path; - + $FAUX_DATA_PATH = THINKUP_WEBAPP_PATH.'plugins/facebook/tests/testdata/'; $url = preg_replace('/([\?\&])access_token\=[^\?\&]+([\?\&])*/', "$1", $url); $url = preg_replace('/[\?\&]$/', '', $url); - $url = str_replace('https://graph.facebook.com/', '', $url); //$url = str_replace('?access_token=fauxaccesstoken', '', $url); $url = str_replace('/', '_', $url); diff --git a/webapp/plugins/facebook/tests/testdata/100001078428730 b/webapp/plugins/facebook/tests/testdata/100001078428730 index 870beded84..a5cb4d86c1 100644 --- a/webapp/plugins/facebook/tests/testdata/100001078428730 +++ b/webapp/plugins/facebook/tests/testdata/100001078428730 @@ -5,6 +5,7 @@ "last_name": "Snively", "link": "http://www.facebook.com/profile.php?id=100001078428730", "gender": "female", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-10-01T19:40:24+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/100001241452656 b/webapp/plugins/facebook/tests/testdata/100001241452656 index 2191e76f92..bd073a27d4 100644 --- a/webapp/plugins/facebook/tests/testdata/100001241452656 +++ b/webapp/plugins/facebook/tests/testdata/100001241452656 @@ -6,6 +6,7 @@ "link": "https://www.facebook.com/turkishTheajan", "username": "turkishTheajan", "gender": "male", + "birthday": "01/02/03", "locale": "tr_TR", "updated_time": "2011-08-29T13:08:01+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/1034402187 b/webapp/plugins/facebook/tests/testdata/1034402187 index 61927f54f8..6aa7b8344a 100644 --- a/webapp/plugins/facebook/tests/testdata/1034402187 +++ b/webapp/plugins/facebook/tests/testdata/1034402187 @@ -6,6 +6,7 @@ "last_name": "Carrico", "link": "http://www.facebook.com/marcy.carrico", "gender": "female", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-09-17T11:36:25+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/1036552729 b/webapp/plugins/facebook/tests/testdata/1036552729 index 2f0836d5b8..c3e450d187 100644 --- a/webapp/plugins/facebook/tests/testdata/1036552729 +++ b/webapp/plugins/facebook/tests/testdata/1036552729 @@ -6,6 +6,7 @@ "link": "https://www.facebook.com/matthew.fleisher", "username": "matthew.fleisher", "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2011-08-06T02:48:49+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/107982202636989-metadata=true b/webapp/plugins/facebook/tests/testdata/107982202636989-metadata=true index e6112015bb..8c732f4742 100644 --- a/webapp/plugins/facebook/tests/testdata/107982202636989-metadata=true +++ b/webapp/plugins/facebook/tests/testdata/107982202636989-metadata=true @@ -1,6 +1,8 @@ { "id": "107982202636989", "name": "Sample Cause", + "gender": "", + "birthday": "", "picture": "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-snc4/276798_107982202636989_4098521_s.jpg", "link": "https://www.facebook.com/pages/Sample-Cause/107982202636989", "likes": 1, diff --git a/webapp/plugins/facebook/tests/testdata/1184584231 b/webapp/plugins/facebook/tests/testdata/1184584231 index 812089ae4a..128f271120 100644 --- a/webapp/plugins/facebook/tests/testdata/1184584231 +++ b/webapp/plugins/facebook/tests/testdata/1184584231 @@ -6,6 +6,7 @@ "link": "http://www.facebook.com/bill.goodman", "about": "You thought I had my eyes closed but I was looking at you the whole fucking time \n-Ed Vedder", "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-09-29T23:49:44+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/1316858804 b/webapp/plugins/facebook/tests/testdata/1316858804 index cbeacc304c..9fe2f04a46 100644 --- a/webapp/plugins/facebook/tests/testdata/1316858804 +++ b/webapp/plugins/facebook/tests/testdata/1316858804 @@ -3,6 +3,8 @@ "name": "Zack Murtha", "first_name": "Zack", "last_name": "Murtha", + "gender": "male", + "birthday": "01/02/03", "link": "http://www.facebook.com/profile.php?id=1316858804", "about": "\"Life is tough, but it's tougher if you're stupid. \"", "locale": "en_US", diff --git a/webapp/plugins/facebook/tests/testdata/1333740280 b/webapp/plugins/facebook/tests/testdata/1333740280 index 5897f005d4..4ac3f34db7 100644 --- a/webapp/plugins/facebook/tests/testdata/1333740280 +++ b/webapp/plugins/facebook/tests/testdata/1333740280 @@ -5,7 +5,8 @@ "last_name": "Sablok", "link": "https://www.facebook.com/ankit.sablok", "username": "ankit.sablok", - "gender": "male", + "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2011-08-21T09:36:51+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_comments b/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_comments index 4d7a932d66..71b1aae917 100644 --- a/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_comments +++ b/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_comments @@ -4,7 +4,9 @@ "id": "144568048938151_1230814", "from": { "name": "Brian Poole", - "id": "510971571" + "id": "510971571", + "gender": "male", + "birthday": "01/02/03" }, "message": "Congrats to all and happy hacking!", "created_time": "2011-02-06T00:06:11+0000" @@ -13,7 +15,9 @@ "id": "144568048938151_1230823", "from": { "name": "Aditya Permana", - "id": "1302210867" + "id": "1302210867", + "gender": "female", + "birthday": "01/02/03" }, "message": "first", "created_time": "2011-02-06T00:07:23+0000" @@ -22,7 +26,9 @@ "id": "144568048938151_1230826", "from": { "name": "Imen En Tu Estereo", - "id": "100000576705814" + "id": "100000576705814", + "gender": "male", + "birthday": "01/02/03" }, "message": "yeah\u00a1\u00a1", "created_time": "2011-02-06T00:07:32+0000" @@ -31,7 +37,9 @@ "id": "144568048938151_1230836", "from": { "name": "Jarrod Parker", - "id": "24409159" + "id": "24409159", + "gender": "male", + "birthday": "01/02/03" }, "message": "lmao only 150 made it out of 1673 or whatever it was", "created_time": "2011-02-06T00:08:49+0000" @@ -40,7 +48,9 @@ "id": "144568048938151_1230837", "from": { "name": "Jarrod Parker", - "id": "24409159" + "id": "24409159", + "gender": "male", + "birthday": "01/02/03" }, "message": "well, got one right", "created_time": "2011-02-06T00:09:00+0000" @@ -49,7 +59,9 @@ "id": "144568048938151_1230852", "from": { "name": "Ville Hellman", - "id": "517413587" + "id": "517413587", + "gender": "male", + "birthday": "01/02/03" }, "message": "Congrats to the top 150 for correct solutions, disappointed that some problem definitions changed halfway through the competition...", "created_time": "2011-02-06T00:11:16+0000", @@ -59,7 +71,9 @@ "id": "144568048938151_1230861", "from": { "name": "Gutzsu Lyla Yusuf", - "id": "100000058303131" + "id": "100000058303131", + "gender": "female", + "birthday": "01/02/03" }, "message": "prettttttt", "created_time": "2011-02-06T00:12:25+0000" @@ -68,7 +82,9 @@ "id": "144568048938151_1230878", "from": { "name": "Steve Wallis", - "id": "818460536" + "id": "818460536", + "gender": "male", + "birthday": "01/02/03" }, "message": "It would be nice if they could give us the stats on how many qualified people actually participated in each round. (e.g. this round had 1,673 qualified people. How many of them actually participated during the 3-hour duration? It's obviously more than the 364 people who submitted something.)", "created_time": "2011-02-06T00:14:42+0000", @@ -78,7 +94,9 @@ "id": "144568048938151_1230880", "from": { "name": "Andre Holzner", - "id": "100000773664809" + "id": "100000773664809", + "gender": "male", + "birthday": "01/02/03" }, "message": "interesting.. I see one O(N *M) solution for problem 1 which has passed...", "created_time": "2011-02-06T00:15:35+0000", @@ -88,7 +106,9 @@ "id": "144568048938151_1230886", "from": { "name": "Brian Poole", - "id": "510971571" + "id": "510971571", + "gender": "male", + "birthday": "01/02/03" }, "message": "So I've got a biased idea! How about everyone who participated in round 2 gets a tshirt. I mean.. it's only 64 more tshirts than anticipated!", "created_time": "2011-02-06T00:16:09+0000", @@ -98,7 +118,9 @@ "id": "144568048938151_1230887", "from": { "name": "Audrey Pierrot", - "id": "667404345" + "id": "667404345", + "gender": "male", + "birthday": "01/02/03" }, "message": "I can't :(((", "created_time": "2011-02-06T00:16:13+0000" @@ -107,7 +129,9 @@ "id": "144568048938151_1230888", "from": { "name": "Francisco Fernandez Berrocal", - "id": "1452734980" + "id": "1452734980", + "gender": "male", + "birthday": "01/02/03" }, "message": "Too brutal questions!", "created_time": "2011-02-06T00:16:26+0000", @@ -117,7 +141,9 @@ "id": "144568048938151_1230890", "from": { "name": "Arnab Bose", - "id": "665721288" + "id": "665721288", + "gender": "male", + "birthday": "01/02/03" }, "message": "Andre - Interesting... where?", "created_time": "2011-02-06T00:16:53+0000", @@ -127,7 +153,9 @@ "id": "144568048938151_1230891", "from": { "name": "Daniel Saunders", - "id": "1123122807" + "id": "1123122807", + "gender": "male", + "birthday": "01/02/03" }, "message": "Google chrome ......your mine", "created_time": "2011-02-06T00:17:07+0000" @@ -136,7 +164,9 @@ "id": "144568048938151_1230894", "from": { "name": "Eric Alejandro Destefanis", - "id": "1123359465" + "id": "1123359465", + "gender": "male", + "birthday": "01/02/03" }, "message": "Hey! somehow, my source is in the place of my output (and the output in the place of my source)... does anyone know if this is rejudgable? Or who may I email?", "created_time": "2011-02-06T00:18:09+0000" @@ -145,7 +175,9 @@ "id": "144568048938151_1230907", "from": { "name": "Hongliang Liu", - "id": "682021444" + "id": "682021444", + "gender": "female", + "birthday": "01/02/03" }, "message": "\u0040Eric, just forget about it. Have fun in your weekend.", "created_time": "2011-02-06T00:20:44+0000", @@ -155,7 +187,9 @@ "id": "144568048938151_1230919", "from": { "name": "Arnab Bose", - "id": "665721288" + "id": "665721288", + "gender": "male", + "birthday": "01/02/03" }, "message": "Eric, I'd say it should definitely be considered (though that's my personal opinion). Hopefully the organizers will see your post here!", "created_time": "2011-02-06T00:22:58+0000", @@ -165,7 +199,9 @@ "id": "144568048938151_1230928", "from": { "name": "Kamil Sadkowski", - "id": "100000694633914" + "id": "100000694633914", + "gender": "female", + "birthday": "01/02/03" }, "message": "\u0040Andre Holzner I see even more than one.", "created_time": "2011-02-06T00:25:56+0000", @@ -175,7 +211,9 @@ "id": "144568048938151_1230935", "from": { "name": "Arnab Bose", - "id": "665721288" + "id": "665721288", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Andre, Kamil - I wonder what computer / language they were using. In my system, which is fairly modern, running _empty_ nested loop of 250000 iterations each takes (estimated) 30-40 minutes.", "created_time": "2011-02-06T00:27:58+0000" @@ -184,7 +222,9 @@ "id": "144568048938151_1230936", "from": { "name": "Greg Knox", - "id": "510782039" + "id": "510782039", + "gender": "male", + "birthday": "01/02/03" }, "message": "After looking at some of these solutions, don't feel so bad about not getting any of them right -- Well done anyone who actually got any of these things :)", "created_time": "2011-02-06T00:28:14+0000", @@ -194,7 +234,9 @@ "id": "144568048938151_1230946", "from": { "name": "Jozef Vodicka", - "id": "1240057308" + "id": "1240057308", + "gender": "male", + "birthday": "01/02/03" }, "message": "Congrats guys, enjoy Palo Alto & Hacker Cup T-Shirt :)", "created_time": "2011-02-06T00:30:37+0000" @@ -203,7 +245,9 @@ "id": "144568048938151_1230952", "from": { "name": "Erik Ramsgaard Wognsen", - "id": "738668796" + "id": "738668796", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Brian Poole: 364 is the number of people who uploaded anything; the number of participants is larger. But yeah, t-shirts ftw :-P", "created_time": "2011-02-06T00:31:20+0000" @@ -212,7 +256,9 @@ "id": "144568048938151_1230956", "from": { "name": "Robert Burke", - "id": "23917679" + "id": "23917679", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Brian: No, a lot of people did the contest and got 0 correct answers and 0 wrong answers. 364 is only the number of people who got 1 or more answers.", "created_time": "2011-02-06T00:32:08+0000" @@ -221,7 +267,9 @@ "id": "144568048938151_1230959", "from": { "name": "Steve Wallis", - "id": "818460536" + "id": "818460536", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Andre, Kamil, Arnab... perhaps they knew a way to download the source file without starting the timer, like in previous rounds?", "created_time": "2011-02-06T00:33:55+0000" @@ -230,7 +278,9 @@ "id": "144568048938151_1230965", "from": { "name": "Edward T. Tonai", - "id": "520613319" + "id": "520613319", + "gender": "male", + "birthday": "01/02/03" }, "message": "I think that the 5 people who solved 2 problems who weren't in the top 25 should get invited to the finals anyway. I wasn't one of them, btw. Considering how many people failed at every stage, and how we weren't even close to 3000 in round 2, and only 150 are getting a T-Shirt. I think solving 2 is a major major accomplishment!", "created_time": "2011-02-06T00:37:01+0000", diff --git a/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_likes b/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_likes index 2ee656a050..bebfc1a3c0 100644 --- a/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_likes +++ b/webapp/plugins/facebook/tests/testdata/133954286636768_144568048938151_likes @@ -2,11 +2,15 @@ "data": [ { "id": "100002586603826", - "name": "Sade Tolgahan" + "name": "Sade Tolgahan", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100000931059876", - "name": "Kosso Thierry" + "name": "Kosso Thierry", + "gender": "male", + "birthday": "01/02/03" }, ], "paging": { diff --git a/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=MjY= b/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=MjY= index cc3a53bfac..2a852973b4 100644 --- a/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=MjY= +++ b/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=MjY= @@ -4,7 +4,9 @@ "id": "775180192497884_104932845", "from": { "name": "Ayush Shrivastava", - "id": "100005963640296" + "id": "100005963640296", + "gender": "female", + "birthday": "01/02/03" }, "message": "Do you want to hack somebody's facebook account? Use this program that can steal facebook password 100% Working here http://fbhackerz.blogspot.com/", "can_remove": false, diff --git a/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=NTQ= b/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=NTQ= index af3ed972bc..9cacf0e6d8 100644 --- a/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=NTQ= +++ b/webapp/plugins/facebook/tests/testdata/133954286636768_775180192497884_comments-limit=25-after=NTQ= @@ -4,7 +4,9 @@ "id": "775180192497884_104932844", "from": { "name": "Ayush Shrivastava", - "id": "100005963640296" + "id": "100005963640296", + "gender": "female", + "birthday": "01/02/03" }, "message": "Do you want to hack somebody's facebook account? Use this program that can steal facebook password 100% Working here http://fbhackerz.blogspot.com/", "can_remove": false, @@ -16,7 +18,9 @@ "id": "775180192497884_104932623", "from": { "name": "Michael Gidron", - "id": "1144454524" + "id": "1144454524", + "gender": "male", + "birthday": "01/02/03" }, "message": "wow.. I over looked the single # sign as being a valid square.", "can_remove": false, @@ -28,7 +32,10 @@ "id": "775180192497884_104932083", "from": { "name": "Hafiz Ahmad", - "id": "100000987379715" + "id": "100000987379715", + "gender": "male", + "birthday": "01/02/03" + }, "message": "<<<<<<<<\nHack facebook account online,You can hack facebook passwords for free with this online hacking tool. No download needed,Hack facebook account online,check this site :- http://www.hack-fb-online.com\n<<<<<<<<", "can_remove": false, @@ -40,7 +47,9 @@ "id": "775180192497884_104931489", "from": { "name": "IntÄ“rnàTïônàl Bräñðêð Bâçhíì", - "id": "100002169050570" + "id": "100002169050570", + "gender": "", + "birthday": "" }, "message": "i need help. if any know how to to hack fb page. inbox me :'(", "can_remove": false, @@ -52,7 +61,9 @@ "id": "775180192497884_104930825", "from": { "name": "Marcin KrnÄ…bny", - "id": "100001089217022" + "id": "100001089217022", + "gender": "male", + "birthday": "01/02/03" }, "message": "Download: http://www.sharfiles.com/download/2/2983000/Hack", "can_remove": false, @@ -64,7 +75,9 @@ "id": "775180192497884_104930684", "from": { "name": "Anubhav Joshi", - "id": "100000429699776" + "id": "100000429699776", + "gender": "male", + "birthday": "01/02/03" }, "message": "can anybody tell what does source with a red cross signify??? Please reply..", "can_remove": false, @@ -76,7 +89,9 @@ "id": "775180192497884_104930656", "from": { "name": "Yoandry Martinez Rodriguez", - "id": "100000071074557" + "id": "100000071074557", + "gender": "male", + "birthday": "01/02/03" }, "message": "I think -with all respect- you my friend did not completely understood the problem.", "can_remove": false, @@ -88,7 +103,9 @@ "id": "775180192497884_104930589", "from": { "name": "Anis Imanis", - "id": "100001420300757" + "id": "100001420300757", + "gender": "male", + "birthday": "01/02/03" }, "message": "dam i forget it !", "can_remove": false, @@ -100,7 +117,9 @@ "id": "775180192497884_104930496", "from": { "name": "Narendra Yadav", - "id": "100005808700499" + "id": "100005808700499", + "gender": "female", + "birthday": "01/02/03" }, "message": "Facebook Hacker Cup Pl upload input and outfull file of these problems.", "message_tags": [ @@ -121,7 +140,9 @@ "id": "775180192497884_104930475", "from": { "name": "Atul Anand", - "id": "855354123" + "id": "855354123", + "gender": "male", + "birthday": "01/02/03" }, "message": "for square detection problem, i tweaked the classic algorithm of finding maximum square within a matrix with all ones i.e min(DP[i-1][j-1],DP[i-1][j] , DP[i][j-])+1 if input[i][j]==1.To make sure that we have found the max square , we just need to take care that DP[i-1][j-1],DP[i][j-1],DP[i-1][j] are equal . keep updating x-axis square boundary and y-axis square boundary.now just traverse the whole input[][] once again so that there is no more '1' (i.e #) expect the final square bounday found using DP[][].", "can_remove": false, @@ -133,7 +154,9 @@ "id": "775180192497884_104930266", "from": { "name": "Diego Arcos", - "id": "100006184567958" + "id": "100006184567958", + "gender": "male", + "birthday": "01/02/03" }, "message": "Alejandro R. Arzola ya viste los problemas tipo icpc, y las soluciones!", "message_tags": [ @@ -154,7 +177,9 @@ "id": "775180192497884_104930125", "from": { "name": "Muhammad Ridwan Apriansyah", - "id": "100000075861819" + "id": "100000075861819", + "gender": "male", + "birthday": "01/02/03" }, "message": "Well i prefer to use deque and do some simple simulation to attack problem 2 :v", "can_remove": false, @@ -166,7 +191,9 @@ "id": "775180192497884_104929872", "from": { "name": "Eduardo Cuesta", - "id": "100001049452062" + "id": "100001049452062", + "gender": "male", + "birthday": "01/02/03" }, "message": "I have a question about the following assertion in Tennison problem:\n\n\"Luckily for Tennison, whenever he wins a set, the probability that there will be sun increases by pu with probability pw. Unfortunately, when Tennison loses a set, the probability of sun decreases by pd with probability pl.\"\n\nDoes this mean that when Tennison wins a set (that is P(win) is 1) then P(sun) += pu * pw else (that is 1 - P(win) is 0) then P(sun) -= pd * pl?\n\nIf so then the problem can be solved with less temporal and spatial complexity by a FORMULA.", "can_remove": false, @@ -178,7 +205,9 @@ "id": "775180192497884_104929781", "from": { "name": "Muhammad Shufi", - "id": "100006628740846" + "id": "100006628740846", + "gender": "male", + "birthday": "01/02/03" }, "message": "Nice", "can_remove": false, @@ -190,7 +219,9 @@ "id": "775180192497884_104929756", "from": { "name": "Anubhav Joshi", - "id": "100000429699776" + "id": "100000429699776", + "gender": "male", + "birthday": "01/02/03" }, "message": "why is my second question wrong it uses the same algorithm????", "can_remove": false, @@ -202,7 +233,9 @@ "id": "775180192497884_104929542", "from": { "name": "Victor Borisov", - "id": "100001024632246" + "id": "100001024632246", + "gender": "male", + "birthday": "01/02/03" }, "message": "Ð¢Ñ€ÐµÑ‚ÑŒÑ Ð·Ð°Ð´Ð°Ñ‡Ð° Ñто кабзец какой-то проÑто!", "can_remove": false, @@ -214,7 +247,9 @@ "id": "775180192497884_104929509", "from": { "name": "Omar Rivera", - "id": "1565831002" + "id": "1565831002", + "gender": "male", + "birthday": "01/02/03" }, "message": "The order players in Basketball game result is important?", "can_remove": false, @@ -226,7 +261,9 @@ "id": "775180192497884_104929449", "from": { "name": "Baljeet Singh", - "id": "100007113308231" + "id": "100007113308231", + "gender": "male", + "birthday": "01/02/03" }, "message": "My Solution for Square Detector give the same output as accepted solution but My final solution is not accepted.Correct me if I am wrong.Thanks in advance. http://ideone.com/tjcp0D", "can_remove": false, @@ -238,7 +275,9 @@ "id": "775180192497884_104929272", "from": { "name": "Durgesh Kumar", - "id": "100001847357742" + "id": "100001847357742", + "gender": "male", + "birthday": "01/02/03" }, "message": "the 4th case is not cleared...... \"sixth decimal place\"... output gives Case #4: 0.999954 but Case #4: 0.999956. why??", "can_remove": false, @@ -250,7 +289,9 @@ "id": "775180192497884_104929213", "from": { "name": "Ollo Akhtom", - "id": "100003231711648" + "id": "100003231711648", + "gender": "male", + "birthday": "01/02/03" }, "message": "I used the same approach stated above.But the Fourth case gives me wrong output for the sixth decimal place. My output gives Case #4: 0.999954 .It should have been Case #4: 0.999956 , I don't know why ?", "can_remove": false, @@ -262,7 +303,9 @@ "id": "775180192497884_104929196", "from": { "name": "David Machaj", - "id": "6203047" + "id": "6203047", + "gender": "male", + "birthday": "01/02/03" }, "message": "I agree with the \"run out of time\" comment for Tennyson. At least half of the time I spent on that problems was optimizing and parallelizing it to handle 100 cases of 100 rounds to be sure that my program could finish in under 6 minutes (C#). In the end I only got 20 cases and it took 15 seconds to run. ", "can_remove": false, @@ -274,7 +317,9 @@ "id": "775180192497884_104929194", "from": { "name": "Zakka Fauzan Muhammad", - "id": "1177864071" + "id": "1177864071", + "gender": "male", + "birthday": "01/02/03" }, "message": "can anyone explain the third answer in more detail?", "can_remove": false, @@ -286,7 +331,9 @@ "id": "775180192497884_104929160", "from": { "name": "Irvan", - "id": "1548822479" + "id": "1548822479", + "gender": "male", + "birthday": "01/02/03" }, "message": "Thanks for your solution \"No.3\" -_- Perfect ! you win .", "can_remove": false, @@ -298,7 +345,9 @@ "id": "775180192497884_104929134", "from": { "name": "Ashutosh Kumar", - "id": "100001917451288" + "id": "100001917451288", + "gender": "male", + "birthday": "01/02/03" }, "message": "Can some one explain me how they have tried to solve square detector", "can_remove": false, @@ -310,7 +359,9 @@ "id": "775180192497884_104929131", "from": { "name": "Kondal Chinnu", - "id": "100002177566168" + "id": "100002177566168", + "gender": "male", + "birthday": "01/02/03" }, "message": "Hi hackers", "can_remove": false, diff --git a/webapp/plugins/facebook/tests/testdata/133954286636768_feed b/webapp/plugins/facebook/tests/testdata/133954286636768_feed index 0a7d2a7038..c861f1f235 100644 --- a/webapp/plugins/facebook/tests/testdata/133954286636768_feed +++ b/webapp/plugins/facebook/tests/testdata/133954286636768_feed @@ -39,103 +39,153 @@ "data": [ { "id": "100002526577162", - "name": "Sultan Bajalan" + "name": "Sultan Bajalan", + "gender": "male", + "birthday": "01/02/03" }, { "id": "632661716", - "name": "Riham Magdy" + "name": "Riham Magdy", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100000063019464", - "name": "Jinsean Tee" + "name": "Jinsean Tee", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1228678051", - "name": "Warsito Raharjo" + "name": "Warsito Raharjo", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1664847556", - "name": "Fn Lion" + "name": "Fn Lion", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100003738152592", - "name": "Marc Albrightun" + "name": "Marc Albrightun", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002077698098", - "name": "Hardik Patel" + "name": "Hardik Patel", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100007089823970", - "name": "Digvijay Kirti Verma" + "name": "Digvijay Kirti Verma", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1409951512", - "name": "Amal Dev" + "name": "Amal Dev", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100001875705436", - "name": "Akhmad Amin" + "name": "Akhmad Amin", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100000396618817", - "name": "Haron Shihab" + "name": "Haron Shihab", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1727363100", - "name": "Raktai Choomeechai" + "name": "Raktai Choomeechai", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100005041530255", - "name": "Julio César Vidal Garcia" + "name": "Julio César Vidal Garcia", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100006846763128", - "name": "Sritanu Chakraborty" + "name": "Sritanu Chakraborty", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100003184362516", - "name": "Praveen Minz" + "name": "Praveen Minz", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100003122999128", - "name": "Raphael Miranda" + "name": "Raphael Miranda", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100004268793723", - "name": "Hafidz SA" + "name": "Hafidz SA", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002366359603", - "name": "Mohamad Fazeli" + "name": "Mohamad Fazeli", + "gender": "male", + "birthday": "01/02/03" }, { "id": "750619142", - "name": "Khristian Fabian Robayo Garcia" + "name": "Khristian Fabian Robayo Garcia", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100000641970489", - "name": "Pankaj Jakhar" + "name": "Pankaj Jakhar", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1187442680", - "name": "Tri Nguyen" + "name": "Tri Nguyen", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100006262693277", - "name": "Ramadan" + "name": "Ramadan", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1496594985", - "name": "Ramun Berger" + "name": "Ramun Berger", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100000642606673", - "name": "Parth Soni" + "name": "Parth Soni", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002365850833", - "name": "Daniel Joshua" + "name": "Daniel Joshua", + "gender": "male", + "birthday": "01/02/03" } ], "paging": { @@ -152,7 +202,9 @@ "id": "775180192497884_104928997", "from": { "name": "Ahmed Gamal", - "id": "1046294762" + "id": "1046294762", + "gender": "male", + "birthday": "01/02/03" }, "message": "I hate this Tennison", "can_remove": false, @@ -164,7 +216,9 @@ "id": "775180192497884_104929055", "from": { "name": "Daniel Zhi", - "id": "1306697257" + "id": "1306697257", + "gender": "male", + "birthday": "01/02/03" }, "message": "Agree that Basketball game requires no simulation. You can simply put players in playing order and use modular to identify top P after M rounds.", "can_remove": false, @@ -176,7 +230,9 @@ "id": "775180192497884_104929597", "from": { "name": "Eugene Serkin", - "id": "100000076041242" + "id": "100000076041242", + "gender": "male", + "birthday": "01/02/03" }, "message": "Would be interesting to see programming language statistics at the end of the tournament.", "can_remove": false, @@ -188,7 +244,9 @@ "id": "775180192497884_104929254", "from": { "name": "Roman Vorobets", - "id": "100001065080101" + "id": "100001065080101", + "gender": "male", + "birthday": "01/02/03" }, "message": "There are not enough tests for problem 1!\nMy colleague's solution got accepted, even though it returns 'YES' for the following test input:\n1\n3\n#.#\n...\n#.#", "can_remove": false, @@ -200,7 +258,9 @@ "id": "775180192497884_104929012", "from": { "name": "Gal Appelbaum", - "id": "740952671" + "id": "740952671", + "gender": "male", + "birthday": "01/02/03" }, "message": "You should also add the questions on top of the solution explanation so people can read the question again before reading the explanation, or if they don't understand something they can look back at the question.", "can_remove": false, @@ -212,7 +272,9 @@ "id": "775180192497884_104929336", "from": { "name": "Shreyas Subramaniam", - "id": "682765760" + "id": "682765760", + "gender": "male", + "birthday": "01/02/03" }, "message": "The basketball problem becomes a whole lot simpler if you use a min-heap.", "can_remove": false, @@ -224,7 +286,9 @@ "id": "775180192497884_104932142", "from": { "name": "Maria Jose Patron", - "id": "1170794705" + "id": "1170794705", + "gender": "female", + "birthday": "01/02/03" }, "message": "Angel :P", "message_tags": [ @@ -245,7 +309,9 @@ "id": "775180192497884_104929049", "from": { "name": "Mahsum Yılmaz", - "id": "100000840472540" + "id": "100000840472540", + "gender": "male", + "birthday": "01/02/03" }, "message": "Nice :)", "can_remove": false, @@ -257,7 +323,9 @@ "id": "775180192497884_104930492", "from": { "name": "Ahmed Fahmy", - "id": "1544147638" + "id": "1544147638", + "gender": "male", + "birthday": "01/02/03" }, "message": "Basketball ,, just reverse the first K then use modulus \nit doesn't need to be that difficult !", "can_remove": false, @@ -269,7 +337,9 @@ "id": "775180192497884_104930663", "from": { "name": "Arman Prayudi", - "id": "100006804304956" + "id": "100006804304956", + "gender": "male", + "birthday": "01/02/03" }, "message": "mas, bantuin menghacking facebook ya... rumah tangga saya terancam. please inbox balesan caranya.. tolong ya mas admin.", "can_remove": false, @@ -281,7 +351,9 @@ "id": "775180192497884_104929439", "from": { "name": "Andrey Nevolin", - "id": "1614833825" + "id": "1614833825", + "gender": "male", + "birthday": "01/02/03" }, "message": "Hello Facebook!\r\nHow can I contact the author of \"Tennison\" problem?\r\nLooks like exists solution for this problem that makes 10 000 times less multiplications (in worst case) and gives much more accurate results than author's one. Additional accuracy in this solution doesn't allow to pass author's testing. I'd like to discuss this solution with the author", "can_remove": false, @@ -293,7 +365,9 @@ "id": "775180192497884_104929421", "from": { "name": "Katalin Brányiné Sulák", - "id": "100007139493100" + "id": "100007139493100", + "gender": "female", + "birthday": "01/02/03" }, "message": "Tennison: it is very easy to calculate it in an array of K*3+2 rows and 1001 coloumns for sun probabilities, and in 2*K-1 steps of 4*(K+1)*1001 calculation each, using something similar recursive method as mentioned above, but doing it blind for all the elements regardless whether they are 0 or not. (The \"important\" part of the table is sliding so that it has enough place to do the \"inside-line\" modifications in the case of a losing. Every step is calculated - after adding the containt of the row, which represents the K won sets in that step, to the total sum - from the most possible won sets to 0 direction.) For the given 20 testcases this program runs less than 5 seconds written in java. (Actually I won't get the points for this solution because I made accidentally a truncation to 5 and not to 6 decimals...)", "can_remove": false, @@ -305,7 +379,9 @@ "id": "775180192497884_104929371", "from": { "name": "Dmitry Stepanenko", - "id": "100000241245915" + "id": "100000241245915", + "gender": "male", + "birthday": "01/02/03" }, "message": "2 FB Team: guys, you forgot to send emails with reminder about this round :)", "can_remove": false, @@ -317,7 +393,9 @@ "id": "775180192497884_104929320", "from": { "name": "René van den Berg", - "id": "100000015903907" + "id": "100000015903907", + "gender": "male", + "birthday": "01/02/03" }, "message": "After having a look at the solutions given by the top 10, I conclude there is still much to learn about writing elegant solutions. For me, at least :)", "can_remove": false, @@ -329,7 +407,9 @@ "id": "775180192497884_104929256", "from": { "name": "Kamil DÄ™bowski", - "id": "100002474965959" + "id": "100002474965959", + "gender": "female", + "birthday": "01/02/03" }, "message": "please clarify:\nFAQ: \"Round 1: The top 500 finishers will advance to Round 2. Everyone that gets the same NUMBER OF POINTS as the person in 500th place will also advance to Round 2.\"\ne-mail: \"anyone who correctly solves as many PROBLEMS as the competitor in 500th place will also advance to Round 2.\"", "can_remove": false, @@ -341,7 +421,9 @@ "id": "775180192497884_104929004", "from": { "name": "Parker Zhang", - "id": "100000489555914" + "id": "100000489555914", + "gender": "male", + "birthday": "01/02/03" }, "message": "For problem 2. no simulation is required though.", "can_remove": false, @@ -353,7 +435,9 @@ "id": "775180192497884_104934575", "from": { "name": "Valerie Tumnous", - "id": "100006602342768" + "id": "100006602342768", + "gender": "male", + "birthday": "01/02/03" }, "message": "This man has contacts with minors everyday on facebook. He has been ordered to have \"no contact\" with minors. He is on Facebook everyday talking with minors. How can you hackers take him out. It is an obvious violation. But it is a violation the community Pulaski Virginia, does not care about or have the resources to care about. http://www.roanoke.com/news/nrv/2111811-12/former-pulaski-county-volunteer-coach-arrested-on-new.html", "can_remove": false, @@ -365,7 +449,9 @@ "id": "775180192497884_104934567", "from": { "name": "Valerie Tumnous", - "id": "100006602342768" + "id": "100006602342768", + "gender": "male", + "birthday": "01/02/03" }, "message": "Now put theory to practice. http://www.roanoke.com/news/nrv/2111811-12/former-pulaski-county-volunteer-coach-arrested-on-new.html", "can_remove": false, @@ -377,7 +463,9 @@ "id": "775180192497884_104934166", "from": { "name": "Ay Se Zarar", - "id": "100007051626693" + "id": "100007051626693", + "gender": "male", + "birthday": "01/02/03" }, "message": "https://www.facebook.com/ApommmM?ref=ts&fref=ts heres 1 to hack", "can_remove": false, @@ -389,7 +477,9 @@ "id": "775180192497884_104934070", "from": { "name": "Jinsean Tee", - "id": "100000063019464" + "id": "100000063019464", + "gender": "male", + "birthday": "01/02/03" }, "message": "helo are you know hack to slotomania coins ???", "can_remove": false, @@ -402,7 +492,9 @@ "from": { "category": "Computers/internet website", "name": "Hacking and Security", - "id": "206586276069602" + "id": "206586276069602", + "gender": "", + "birthday": "" }, "message": ":)", "can_remove": false, @@ -414,7 +506,9 @@ "id": "775180192497884_104933931", "from": { "name": "Çûtê Moi", - "id": "100006399125805" + "id": "100006399125805", + "gender": "male", + "birthday": "01/02/03" }, "message": "Do you want to hack somebody's facebook account? Use this program that can steal facebook password 100% Working here http://fbhackerz.blogspot.com/", "can_remove": false, @@ -426,7 +520,9 @@ "id": "775180192497884_104933865", "from": { "name": "Rockie Abineno", - "id": "100005737810147" + "id": "100005737810147", + "gender": "male", + "birthday": "01/02/03" }, "message": "http://www.fb-hacker.net/hack.php?id=171594", "can_remove": false, @@ -438,7 +534,9 @@ "id": "775180192497884_104933373", "from": { "name": "Adam Ad", - "id": "100001969645357" + "id": "100001969645357", + "gender": "male", + "birthday": "01/02/03" }, "message": "<<<<<<<<\nHack facebook account online,You can hack facebook passwords for free with this online hacking tool. No download needed,Hack facebook account online,check this site :- http://www.hack-fb-online.com\n<<<<<<<<", "can_remove": false, @@ -450,7 +548,9 @@ "id": "775180192497884_104933322", "from": { "name": "Micael Melo", - "id": "100002650086570" + "id": "100002650086570", + "gender": "male", + "birthday": "01/02/03" }, "message": "http://www.youtube.com/watch?v=9GfT5CTgK-Q&feature=youtu.be&hd=1", "can_remove": false, @@ -511,103 +611,153 @@ "data": [ { "id": "100007131154707", - "name": "Ankit Kumar" + "name": "Ankit Kumar", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100004607914407", - "name": "Narendra Agrawal" + "name": "Narendra Agrawal", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100001488021086", - "name": "Rajat Pal" + "name": "Rajat Pal", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100007089823970", - "name": "Digvijay Kirti Verma" + "name": "Digvijay Kirti Verma", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100005227441659", - "name": "Gadieng Theblues Chelsea" + "name": "Gadieng Theblues Chelsea", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100006177341038", - "name": "Okan VVestly" + "name": "Okan VVestly", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1409951512", - "name": "Amal Dev" + "name": "Amal Dev", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002824866487", - "name": "Pradyumna Cheerala" + "name": "Pradyumna Cheerala", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1446396999", - "name": "Bakhodir Ashirmatov" + "name": "Bakhodir Ashirmatov", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002366359603", - "name": "Mohamad Fazeli" + "name": "Mohamad Fazeli", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1228359709", - "name": "Stephan Mühlstrasser" + "name": "Stephan Mühlstrasser", + "gender": "male", + "birthday": "01/02/03" }, { "id": "510454756", - "name": "Yunduz R" + "name": "Yunduz R", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100004211803440", - "name": "Myu Mithapara" + "name": "Myu Mithapara", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100003678582445", - "name": "Shikhor Roy" + "name": "Shikhor Roy", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002606505562", - "name": "Mohammed Al Samawi" + "name": "Mohammed Al Samawi", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100003517224742", - "name": "Tahmid Hamim Ratul" + "name": "Tahmid Hamim Ratul", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100001214007485", - "name": "Ankita Kalantri" + "name": "Ankita Kalantri", + "gender": "female", + "birthday": "01/02/03" }, { "id": "100003250194462", - "name": "Riaz Gull" + "name": "Riaz Gull", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100001173606228", - "name": "Yoga Anand" + "name": "Yoga Anand", + "gender": "male", + "birthday": "01/02/03" }, { "id": "687779353", - "name": "Tarek Ashraf" + "name": "Tarek Ashraf", + "gender": "male", + "birthday": "01/02/03" }, { "id": "1258986227", - "name": "Ziad Ouf" + "name": "Ziad Ouf", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100003617358781", - "name": "Ashfaque Hossain Akand" + "name": "Ashfaque Hossain Akand", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100001324314963", - "name": "Roland Yeghiazaryan" + "name": "Roland Yeghiazaryan", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100002390600599", - "name": "Subhrajyoti Senapati" + "name": "Subhrajyoti Senapati", + "gender": "male", + "birthday": "01/02/03" }, { "id": "100001288311439", - "name": "Betto Lima" + "name": "Betto Lima", + "gender": "male", + "birthday": "01/02/03" } ], "paging": { @@ -624,7 +774,9 @@ "id": "671912336174291_84655150", "from": { "name": "Sarah Eaglesfield", - "id": "1557887825" + "id": "1557887825", + "gender": "female", + "birthday": "01/02/03" }, "message": "You had 7,500 participants... yet 391K people 'like' this page. Doesn't that sugget you need another qualifying round, or at least need to work on the algorhythm to promote Facebook Pages' internal posts?", "can_remove": false, @@ -636,7 +788,9 @@ "id": "671912336174291_84655264", "from": { "name": "Rory O'Logan", - "id": "1076958099" + "id": "1076958099", + "gender": "male", + "birthday": "01/02/03" }, "message": "Darn I thought a single Hash wouldn't be considered a filled square, Oh well.", "can_remove": false, @@ -648,7 +802,9 @@ "id": "671912336174291_84655155", "from": { "name": "Chuá»™t Bạch", - "id": "100004043929299" + "id": "100004043929299", + "gender": "male", + "birthday": "01/02/03" }, "message": "1425 :)", "can_remove": false, @@ -660,7 +816,9 @@ "id": "671912336174291_84656231", "from": { "name": "Eli Mullis", - "id": "1814672596" + "id": "1814672596", + "gender": "female", + "birthday": "01/02/03" }, "message": "Why is my source code suddenly not correct even though I got a green checkmark when I submitted yesterday? You guys need something a little less ambiguous to denote that the source and solution were received but haven't been graded yet.", "can_remove": false, @@ -672,7 +830,9 @@ "id": "671912336174291_84655420", "from": { "name": "Martin Böschen", - "id": "701481590" + "id": "701481590", + "gender": "male", + "birthday": "01/02/03" }, "message": "My solution for square detector is wrong, yet i got the full score for that problem.\nThis input breaks my algorithm:\n#.#\n...\n#.#", "can_remove": false, @@ -684,7 +844,9 @@ "id": "671912336174291_84655257", "from": { "name": "Keyvhinng Espinoza", - "id": "100000839867024" + "id": "100000839867024", + "gender": "male", + "birthday": "01/02/03" }, "message": "Will fb publish an official discussion about the problems ?", "can_remove": false, @@ -696,7 +858,9 @@ "id": "671912336174291_84655229", "from": { "name": "Randy Augustus", - "id": "212404024" + "id": "212404024", + "gender": "male", + "birthday": "01/02/03" }, "message": "So we don't get notified when it starts we have to check the page? Lame.", "can_remove": false, @@ -708,7 +872,9 @@ "id": "671912336174291_84655177", "from": { "name": "Oswaldo Zavala", - "id": "1207729328" + "id": "1207729328", + "gender": "male", + "birthday": "01/02/03" }, "message": "718 =)", "can_remove": false, @@ -720,7 +886,9 @@ "id": "671912336174291_84659341", "from": { "name": "Ankit Kumar", - "id": "100007131154707" + "id": "100007131154707", + "gender": "male", + "birthday": "01/02/03" }, "message": "i want to become.................", "can_remove": false, @@ -732,7 +900,9 @@ "id": "671912336174291_84659121", "from": { "name": "Lasha Lakirbaia", - "id": "1057089246" + "id": "1057089246", + "gender": "female", + "birthday": "01/02/03" }, "message": "There should be a better way to notify people that the contest is coming. I missed it just because I didn't know it was happening. I didn't get an email or anything", "can_remove": false, @@ -744,7 +914,9 @@ "id": "671912336174291_84658912", "from": { "name": "Betsu Melaku", - "id": "601609715" + "id": "601609715", + "gender": "female", + "birthday": "01/02/03" }, "message": "Has Petr Mitrichev participated this year?", "can_remove": false, @@ -756,7 +928,9 @@ "id": "671912336174291_84656855", "from": { "name": "Ramazan AÅŸkın", - "id": "100004948027524" + "id": "100004948027524", + "gender": "male", + "birthday": "01/02/03" }, "message": "https://www.facebook.com/aylin.aslan.77964201?fref=ts allah rızası için bunu patlatıp ÅŸifresini bana gönderin", "can_remove": false, @@ -768,7 +942,9 @@ "id": "671912336174291_84656854", "from": { "name": "Ramazan AÅŸkın", - "id": "100004948027524" + "id": "100004948027524", + "gender": "male", + "birthday": "01/02/03" }, "message": "https://www.facebook.com/aylin.aslan.77964201?fref=ts", "can_remove": false, @@ -780,7 +956,9 @@ "id": "671912336174291_84656105", "from": { "name": "Yogeesh Seralathan", - "id": "100001023588299" + "id": "100001023588299", + "gender": "male", + "birthday": "01/02/03" }, "message": "Hollyshit! i have submitted the unedited version of program :@ :( solution accepted but source code rejected :@ :( #ShitHappens", "can_remove": false, @@ -792,7 +970,9 @@ "id": "671912336174291_84655893", "from": { "name": "Anubhav Joshi", - "id": "100000429699776" + "id": "100000429699776", + "gender": "male", + "birthday": "01/02/03" }, "message": "can anybody tell what does source with a red cross signify??? Please reply..", "can_remove": false, @@ -804,7 +984,9 @@ "id": "671912336174291_84655535", "from": { "name": "Minseok Jang", - "id": "100000878314124" + "id": "100000878314124", + "gender": "male", + "birthday": "01/02/03" }, "message": "3334 :) see u on second round", "can_remove": false, @@ -816,7 +998,9 @@ "id": "671912336174291_84655391", "from": { "name": "Santosh Kumar Siddharth", - "id": "100002196449131" + "id": "100002196449131", + "gender": "male", + "birthday": "01/02/03" }, "message": "Sanjeev >:o", "message_tags": [ @@ -837,7 +1021,9 @@ "id": "671912336174291_84655310", "from": { "name": "Gergely Varsanyi", - "id": "635372289" + "id": "635372289", + "gender": "male", + "birthday": "01/02/03" }, "message": "I wish we could see programming language stats. Or gender, age, country, region and language stats for that matter :)", "can_remove": false, @@ -849,7 +1035,9 @@ "id": "671912336174291_84655253", "from": { "name": "Sathish Palanisamy", - "id": "100000611973398" + "id": "100000611973398", + "gender": "male", + "birthday": "01/02/03" }, "message": "for the second problem my source is wrong.Which means,my entire logic is wrong or there is some specific format for the source? any constrains? Friends please assist me", "can_remove": false, @@ -861,7 +1049,9 @@ "id": "671912336174291_84655252", "from": { "name": "Deepanshu Mehta", - "id": "627562277" + "id": "627562277", + "gender": "male", + "birthday": "01/02/03" }, "message": "When is round 1 is scheduled ?", "can_remove": false, @@ -873,7 +1063,9 @@ "id": "671912336174291_84655224", "from": { "name": "Samarth Agarwal", - "id": "1310613804" + "id": "1310613804", + "gender": "male", + "birthday": "01/02/03" }, "message": "Can someone explain solution to problem 3?", "can_remove": false, @@ -885,7 +1077,9 @@ "id": "671912336174291_84655223", "from": { "name": "Muhammad Arslan Dogar", - "id": "100002867305975" + "id": "100002867305975", + "gender": "male", + "birthday": "01/02/03" }, "message": "i don't want anything but the shirt ......plzzzz", "can_remove": false, @@ -897,7 +1091,9 @@ "id": "671912336174291_84655207", "from": { "name": "Samanun Chotkiatikhun", - "id": "652907192" + "id": "652907192", + "gender": "male", + "birthday": "01/02/03" }, "message": "Hello, Do anyone miss the output format?\nIn the question 1 my friend printf Yes/No instead of YES/NO.\nCould it possible to rejudge it?\n\nThank you very much", "can_remove": false, @@ -909,7 +1105,9 @@ "id": "671912336174291_84655204", "from": { "name": "Carlos Martinez", - "id": "1144500711" + "id": "1144500711", + "gender": "male", + "birthday": "01/02/03" }, "message": "I don't understand, I had the correct output for the first 2 problems, why didn't I qualified?", "can_remove": false, @@ -921,7 +1119,9 @@ "id": "671912336174291_84656942", "from": { "name": "Jesus David Pacheco Cantero", - "id": "100006752292748" + "id": "100006752292748", + "gender": "male", + "birthday": "01/02/03" }, "message": "http://gemhacks.jimdo.com/newgemhack2121", "can_remove": false, diff --git a/webapp/plugins/facebook/tests/testdata/133954286636768_posts b/webapp/plugins/facebook/tests/testdata/133954286636768_posts index 2b7ac4c73e..25f00bac58 100644 --- a/webapp/plugins/facebook/tests/testdata/133954286636768_posts +++ b/webapp/plugins/facebook/tests/testdata/133954286636768_posts @@ -19,19 +19,27 @@ "data": [ { "name": "York Mw", - "id": "100000234593146" + "id": "100000234593146", + "gender": "male", + "birthday": "01/02/03" }, { "name": "Sabbar Khatib", - "id": "100001478065484" + "id": "100001478065484", + "gender": "male", + "birthday": "01/02/03" }, { "name": "Shih-Chiang Lin", - "id": "792519448" + "id": "792519448", + "gender": "male", + "birthday": "01/02/03" }, { "name": "David Sanchez Jimenez", - "id": "100000103487278" + "id": "100000103487278", + "gender": "male", + "birthday": "01/02/03" } ], "count": 97 diff --git a/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=25 b/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=25 index 2012c6efe7..e62a89d15e 100644 --- a/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=25 +++ b/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=25 @@ -4,7 +4,9 @@ "id": "144568048938151_1230967", "from": { "name": "Govind Chandrasekhar", - "id": "508111053" + "id": "508111053", + "gender": "male", + "birthday": "01/02/03" }, "message": "T-shits for those who submitted wrong answers? That's unfair to those who had the foresight to know that their answers were buggy, and hence didn't bother with submission.", "created_time": "2011-02-06T00:37:22+0000", @@ -14,7 +16,9 @@ "id": "144568048938151_1230990", "from": { "name": "Mark Zammata", - "id": "733775875" + "id": "733775875", + "gender": "male", + "birthday": "01/02/03" }, "message": "Cmd -> color a -> h4cker", "created_time": "2011-02-06T00:44:39+0000", @@ -24,7 +28,9 @@ "id": "144568048938151_1230996", "from": { "name": "David Berthelot", - "id": "100000859513781" + "id": "100000859513781", + "gender": "male", + "birthday": "01/02/03" }, "message": "lol a T-shirt for a wrong answer :) I love the sense of humor that some people have here. So you could just upload a bunch of random numbers and printf(\"Hello mum\");. Hehehe", "created_time": "2011-02-06T00:45:49+0000", @@ -34,7 +40,9 @@ "id": "144568048938151_1231021", "from": { "name": "Mark Karpel\u00e8s", - "id": "532974378" + "id": "532974378", + "gender": "male", + "birthday": "01/02/03" }, "message": "shouldn't have done that half alsleep, missed the 6 minutes limit ~", "created_time": "2011-02-06T00:52:00+0000" @@ -43,7 +51,9 @@ "id": "144568048938151_1231024", "from": { "name": "Hongliang Liu", - "id": "682021444" + "id": "682021444", + "gender": "male", + "birthday": "01/02/03" }, "message": "FB financial department is happy to save 150$ T-shirts.", "created_time": "2011-02-06T00:52:05+0000", @@ -53,7 +63,9 @@ "id": "144568048938151_1231071", "from": { "name": "Martin Cifko \u0160tef\u010dek", - "id": "791864198" + "id": "791864198", + "gender": "male", + "birthday": "01/02/03" }, "message": "just give 2 t-shirt to the first 150 people ;-) and no, i'm not one of them", "created_time": "2011-02-06T01:00:26+0000", @@ -63,7 +75,9 @@ "id": "144568048938151_1231075", "from": { "name": "Wayne Colvin", - "id": "503383185" + "id": "503383185", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Steve : Downloading the file always started the timer whether your browser showed it right or not. Those people (including me) knew full well that they were forfeiting a question they couldn't answer if they downloaded the input. :/", "created_time": "2011-02-06T01:01:01+0000" @@ -72,7 +86,9 @@ "id": "144568048938151_1231089", "from": { "name": "Jon Adams", - "id": "1374900264" + "id": "1374900264", + "gender": "male", + "birthday": "01/02/03" }, "message": "there should totally be a pity round for the remaining 150 t-shirts", "created_time": "2011-02-06T01:03:20+0000", @@ -82,7 +98,9 @@ "id": "144568048938151_1231104", "from": { "name": "Pradeep Chelani", - "id": "1393001281" + "id": "1393001281", + "gender": "male", + "birthday": "01/02/03" }, "message": "some people used sine cosine function and some people use left right bit operators to solve first problem...never thought it was so diff to solve large numbers...still unable to understand their logic...can someone explain me", "created_time": "2011-02-06T01:06:38+0000", @@ -92,7 +110,9 @@ "id": "144568048938151_1231110", "from": { "name": "Martin Cifko \u0160tef\u010dek", - "id": "791864198" + "id": "791864198", + "gender": "male", + "birthday": "01/02/03" }, "message": "I don't really get the solution for problem 1, i downloaded a few source codes\nI have the same algorithm, but it's still not fast enough on my computer ;-))", "created_time": "2011-02-06T01:07:37+0000" @@ -101,7 +121,9 @@ "id": "144568048938151_1231136", "from": { "name": "Pradeep Chelani", - "id": "1393001281" + "id": "1393001281", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Martin...same here..some people O(N*M) solution was accepted while mine O(P*P) is not even working on my machine", "created_time": "2011-02-06T01:10:31+0000" @@ -110,7 +132,9 @@ "id": "144568048938151_1231139", "from": { "name": "Martin Cifko \u0160tef\u010dek", - "id": "791864198" + "id": "791864198", + "gender": "male", + "birthday": "01/02/03" }, "message": "i got O(P*L) :-))", "created_time": "2011-02-06T01:11:14+0000" @@ -119,7 +143,9 @@ "id": "144568048938151_1231192", "from": { "name": "Pradeep Chelani", - "id": "1393001281" + "id": "1393001281", + "gender": "male", + "birthday": "01/02/03" }, "message": "i dont think their is much diff in O(P*L) and O(P*P)", "created_time": "2011-02-06T01:16:16+0000" @@ -128,7 +154,9 @@ "id": "144568048938151_1231243", "from": { "name": "Martin Cifko \u0160tef\u010dek", - "id": "791864198" + "id": "791864198", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040pradeep just a little, but in O(P*L) u dont have to check if (i*j\u003cl), because it's always true... anyway that doesn't help...", "created_time": "2011-02-06T01:23:32+0000", @@ -138,7 +166,9 @@ "id": "144568048938151_1231296", "from": { "name": "Wonjohn-Wonjun Choi", - "id": "100000150076278" + "id": "100000150076278", + "gender": "male", + "birthday": "01/02/03" }, "message": "OMGOMGOMOGMOGMOGMG Can we have another subround please? I completely forgot about this T_T", "created_time": "2011-02-06T01:30:12+0000" @@ -147,7 +177,9 @@ "id": "144568048938151_1231312", "from": { "name": "Abhiraj Pande", - "id": "100000492868617" + "id": "100000492868617", + "gender": "male", + "birthday": "01/02/03" }, "message": "Studious student's output and solution was wrong.", "created_time": "2011-02-06T01:32:33+0000" @@ -156,7 +188,9 @@ "id": "144568048938151_1231369", "from": { "name": "Pradeep Chelani", - "id": "1393001281" + "id": "1393001281", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040abhiraj which output is wrong?", "created_time": "2011-02-06T01:40:56+0000" @@ -165,7 +199,9 @@ "id": "144568048938151_1231454", "from": { "name": "Abhiraj Pande", - "id": "100000492868617" + "id": "100000492868617", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040pradeep: they have given that c is a character from substring but they are considering it a or b.", "created_time": "2011-02-06T01:53:02+0000" @@ -174,7 +210,9 @@ "id": "144568048938151_1231734", "from": { "name": "Pradeep Chelani", - "id": "1393001281" + "id": "1393001281", + "gender": "male", + "birthday": "01/02/03" }, "message": "it means c is any character in substring not 'c' as character", "created_time": "2011-02-06T02:29:01+0000", @@ -184,7 +222,9 @@ "id": "144568048938151_1231795", "from": { "name": "Noor Ali", - "id": "1424405954" + "id": "1424405954", + "gender": "male", + "birthday": "01/02/03" }, "message": "Hmmm...sorry I don't participate about this competition. Caused I very busy yesterday. When I can paticipate round again?", "created_time": "2011-02-06T02:35:22+0000" @@ -193,7 +233,9 @@ "id": "144568048938151_1231822", "from": { "name": "Luca Ambrosi", - "id": "510426156" + "id": "510426156", + "gender": "male", + "birthday": "01/02/03" }, "message": "can anyone write here the problem statements pls?", "created_time": "2011-02-06T02:39:10+0000" @@ -202,7 +244,9 @@ "id": "144568048938151_1231841", "from": { "name": "Sam Rose", - "id": "544780370" + "id": "544780370", + "gender": "male", + "birthday": "01/02/03" }, "message": "Maaaaaan they were some difficult problems. Major kudos to anyone that managed to get any of them. I racked my brains on the Bonus Assignment one for 2 and a half hours and got no where even close to the answer ^_^\n\nI'm gonna side with the \"Give everyone from round 2 t-shirts\" party :p", "created_time": "2011-02-06T02:41:57+0000", @@ -213,7 +257,9 @@ "from": { "name": "Erik Ramsgaard Wognsen", "id": "738668796" - }, + },, + "gender": "male", + "birthday": "01/02/03" "message": "Maybe they don't print the t-shirts until they know the sizes of the lucky people, so I don't think any will go to waste either way ^^", "created_time": "2011-02-06T03:30:46+0000" }, @@ -221,7 +267,9 @@ "id": "144568048938151_1232078", "from": { "name": "Anshul Goyal", - "id": "1406403734" + "id": "1406403734", + "gender": "male", + "birthday": "01/02/03" }, "message": "I can't say about T-shirts ..\nBut problems were damn good although I wasn't able to solve any one of them correctly.", "created_time": "2011-02-06T03:30:54+0000" @@ -230,7 +278,9 @@ "id": "144568048938151_1232411", "from": { "name": "Rafael Telles Muller", - "id": "536776067" + "id": "536776067", + "gender": "male", + "birthday": "01/02/03" }, "message": "I guess Facebook will have to keep 150 shirts..LMAO. This first Hacker cup was a complete failure.", "created_time": "2011-02-06T04:17:38+0000" diff --git a/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=50 b/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=50 index 0b65d4226b..68c8b6cb2e 100644 --- a/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=50 +++ b/webapp/plugins/facebook/tests/testdata/144568048938151_comments-limit=25-offset=50 @@ -4,7 +4,9 @@ "id": "144568048938151_1232411", "from": { "name": "Rafael Telles Muller", - "id": "536776067" + "id": "536776067", + "gender": "male", + "birthday": "01/02/03" }, "message": "I guess Facebook will have to keep 150 shirts..LMAO. This first Hacker cup was a complete failure.", "created_time": "2011-02-06T04:17:38+0000" @@ -13,7 +15,9 @@ "id": "144568048938151_1232582", "from": { "name": "Akhil Kunnath", - "id": "800223221" + "id": "800223221", + "gender": "male", + "birthday": "01/02/03" }, "message": "Can anybody pls share all the 3 qns here ?? I'm not able to see the problem statements............", "created_time": "2011-02-06T04:47:37+0000" @@ -22,7 +26,9 @@ "id": "144568048938151_1232840", "from": { "name": "Aditya Permana", - "id": "1302210867" + "id": "1302210867", + "gender": "female", + "birthday": "01/02/03" }, "message": "Little Scott recently learned how to perform arithmetic operations modulo some prime number P. As a training set he picked two sequences a of length N and b of length M, generated in the following way:\na1=A1\na2=A2\nai=(ai-2 * A3 + ai-1*A4 + A5) mod P, for i=3...N\nb1=B1\nb2=B2\nbj=(bj-2 * B3 + bj-1 * B4 + B5) mod P, for j=3...M\n\nNow he wants to find the number of pairs (i, j), where 1 \u2264 i \u2264 N and 1 \u2264 j \u2264 M, such that (ai * bj) mod P \u003c L, for given number L. He asked you to do the same to help him check his answers.\nInput\n\nThe first line of input file consists of a single number T, the number of test cases. Each test consists of three lines. The first line of a test case contains two integers: prime number P and positive integer L. The second line consists of six non-negative integers N, A1, A2, A3, A4, A5. Likewise, the third line contains six non-negative integers M, B1, B2, B3, B4, B5.\nOutput\n\nOutput T lines, with the answer to each test case on a single line.\nConstraints\n\nT = 20\n2 \u2264 P \u003c 250,000\nP is prime\n1 \u2264 L \u2264 P\n2 \u2264 N, M \u2264 10,000,000\n0 \u2264 A1, A2, A3, A4, A5, B1, B2, B3, B4, B5 \u003c P \n\nExample inputExample output\n\n5\n3 1\n4 0 2 2 2 2\n2 1 2 1 0 0\n3 1\n5 2 0 0 1 1\n5 1 1 2 0 0\n3 3\n5 0 0 1 2 2\n3 2 1 1 1 1\n5 1\n5 2 0 4 0 4\n3 2 1 2 4 4\n5 4\n2 2 1 3 1 4\n5 1 0 2 3 3\n\n6\n10\n15\n3\n9", "created_time": "2011-02-06T05:53:29+0000" @@ -31,7 +37,9 @@ "id": "144568048938151_1232842", "from": { "name": "Aditya Permana", - "id": "1302210867" + "id": "1302210867", + "gender": "female", + "birthday": "01/02/03" }, "message": "Studious Student II\n\nYou've decided to make up another string manipulation game instead of paying attention in class. Starting with a string composed entirely of 'a' and 'b' characters, you will iteratively apply the following operation:\n\nFor a string s of length len, choose indices i and j, where i \u003c j \u003c len. Choose a character c that occurs in the substring which begins at zero-based index i of string s and extends to the index j (inclusive). Replace all characters in s with zero-based index in [i, j] with a single instance of c to generate s'. Set s to be s'.\n\nAs an example of sequence of operations consider the string 'abba'. Some of the possible transformations are shown below. The substring being replaced is enclosed in square brackets.\n\n 1. [abb]a \u2192 [aa] \u2192 a\n 2. a[bba] \u2192 [aa] \u2192 a\n 3. ab[ba] \u2192 [abb] \u2192 a\n 4. a[bb]a \u2192 aba\n\nThe goal of your game is simple: calculate how many different sequences of operations you can perform. As this number can be very large, you decide to calculate it modulo 1,000,000,007. Two sequences of operations are considered different if they differ in length, or if they differ in at least one position. Note that the order of operations is a factor. The empty sequence of operations should be counted as well. Operations can be considered triples of (i, j, c) as described above, and these are the only values used when computing whether two operations are the same.\nInput\n\nThe first line of the input file contains a single number N, the number of test cases. Each test case is written on a separate line, and contains a string consisting of letters 'a' and 'b'.\nOutput\n\nOutput N lines, with the answer to each test case on a single line.\nConstraints\n\nN = 20\n1 \u2264 len \u2264 60\ns only contains the lowercase characters 'a' and 'b'.\n\nExample input\n\n5\nab\naba\naabb\nababa\nbbbbb\nExample output\n3\n13\n57\n642\n120", "created_time": "2011-02-06T05:54:21+0000" @@ -40,7 +48,9 @@ "id": "144568048938151_1232846", "from": { "name": "Aditya Permana", - "id": "1302210867" + "id": "1302210867", + "gender": "female", + "birthday": "01/02/03" }, "message": "Bonus Assignments\n\nYou are in charge of a group of N workers, and you want to pay a one-time bonus to each of them. The bonus for each worker is an integer number of dollars. According to state law the bonus of the worker who gets the least should be no less than A, but no more than B. To motivate your staff, the bonus of the worker who gets the most should be no less than C and no more than D.\n\nWorkers tend to spend their entire bonuses on HackerCola. Each of them buys bottles of HackerCola until he runs out of money, i.e., until amount of money left is less than the price of one bottle. Workers are very individualistic and each of them uses his own money only, so they never pool to buy HackerCola. Unfortunately you don't remember the price of one bottle of HackerCola, but you are pretty sure that it is an integer number of dollars greater than 1.\n\nSince you care about the working class you want to assign bonuses to workers in such a way that there would be at least one worker who would have some money left after buying as much HackerCola as possible regardless of the price of the bottle. Calculate the number of possible bonus assignments that fit this constraint. Two bonus assignments are different if at least one worker gets different bonus in each assignment. Since the answer can be large, calculate it modulo 1,000,000,007.\nInput\n\nThe first line of the input contains one integer T, the number of test cases. Each of the next T lines consists of 5 integers separated by spaces: N, A, B, C and D.\nOutput\n\nFor each of the test cases print a line containing number of possible bonus assignments modulo 1,000,000,007.\nConstraints\n\nT = 20\n1 \u2264 N \u2264 10^6\n1 \u2264 A \u2264 B \u2264 10^6\n1 \u2264 C \u2264 D \u2264 10^6\n\nExample input\n5\n2 1 2 4 5\n2 2 4 3 5\n1 5 10 5 10\n5 5 7 2 3\n5 2 7 5 12\nExample output\n6\n10\n0\n0\n149190", "created_time": "2011-02-06T05:55:29+0000" @@ -49,7 +59,9 @@ "id": "144568048938151_1232881", "from": { "name": "Abhiraj Pande", - "id": "100000492868617" + "id": "100000492868617", + "gender": "male", + "birthday": "01/02/03" }, "message": "hey does anyone think studios student is wrong? I think its wrong. Its mentioned that c is a character from substring. Now consider the example of bbbbb. 'a' is not present in this string still 120 solution comes when we replace it with 'a' or 'b'", "created_time": "2011-02-06T06:04:40+0000" @@ -58,7 +70,9 @@ "id": "144568048938151_1232906", "from": { "name": "Vladimir Basunkov", - "id": "100002004060331" + "id": "100002004060331", + "gender": "male", + "birthday": "01/02/03" }, "message": "WOW! Great solutions with FFT for first problem!Really cool=) I feel so stupid...=/", "created_time": "2011-02-06T06:09:12+0000", @@ -68,7 +82,9 @@ "id": "144568048938151_1233264", "from": { "name": "Pradeep Balakrishna", - "id": "1081351919" + "id": "1081351919", + "gender": "male", + "birthday": "01/02/03" }, "message": "can anyone pls share the idea behind scott's new trick", "created_time": "2011-02-06T07:58:13+0000" @@ -77,7 +93,9 @@ "id": "144568048938151_1233290", "from": { "name": "Ananya Mallik", - "id": "1560315625" + "id": "1560315625", + "gender": "male", + "birthday": "01/02/03" }, "message": "O(M*N) passed xD", "created_time": "2011-02-06T08:08:23+0000" @@ -86,7 +104,9 @@ "id": "144568048938151_1234137", "from": { "name": "Parth Shah", - "id": "100001898527166" + "id": "100001898527166", + "gender": "male", + "birthday": "01/02/03" }, "message": "d hell wid these solutions who bloody cares..........", "created_time": "2011-02-06T11:57:22+0000" @@ -95,7 +115,9 @@ "id": "144568048938151_1234324", "from": { "name": "Robert Burke", - "id": "23917679" + "id": "23917679", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Abhiraj: For input 'bbbbb'...\nThere is 1 sequence in which you do nothing.\nThere is 1 sequence in which your first move is [bbbbb].\nThere are 4 sequences in which your first move is [bbbb]b or b[bbbb].\nThere are 18 sequences in which your first move is [bbb]bb, b[bbb]b, or bb[bbb].\nThere are 96 sequences in which your first move is [bb]bbb, b[bb]bb, bb[bb]b, or bbb[bb]\n1+1+4+18+96 = 120", "created_time": "2011-02-06T12:39:03+0000", @@ -105,7 +127,9 @@ "id": "144568048938151_1234441", "from": { "name": "Adam Folwarczny", - "id": "1273118667" + "id": "1273118667", + "gender": "male", + "birthday": "01/02/03" }, "message": "University of Warsaw ... Gz xd", "created_time": "2011-02-06T13:09:37+0000" @@ -114,7 +138,9 @@ "id": "144568048938151_1234621", "from": { "name": "Thomas Dybdahl Ahle", - "id": "755563278" + "id": "755563278", + "gender": "male", + "birthday": "01/02/03" }, "message": "A few people passed with O(P*P) or O(P*L) by using massive parallelization. Kind of cheating perhaps, but then I suppose the problem authors wanted it to be solvable with slightly simpler algorithms than FFT, like Karatsuba.", "created_time": "2011-02-06T13:57:18+0000" @@ -123,7 +149,9 @@ "id": "144568048938151_1234649", "from": { "name": "Thomas Dybdahl Ahle", - "id": "755563278" + "id": "755563278", + "gender": "male", + "birthday": "01/02/03" }, "message": "\u0040Robert Burke I wish they had put explanations under the examples.", "created_time": "2011-02-06T14:03:55+0000" @@ -132,7 +160,9 @@ "id": "144568048938151_1235860", "from": { "name": "Akhil Kunnath", - "id": "800223221" + "id": "800223221", + "gender": "male", + "birthday": "01/02/03" }, "message": "thnx Aditya.. thnx very much..........", "created_time": "2011-02-06T16:42:23+0000" @@ -141,7 +171,9 @@ "id": "144568048938151_1237947", "from": { "name": "Jos\u00e9 Ram\u00f3n Garc\u00eda Alvarado", - "id": "654687405" + "id": "654687405", + "gender": "male", + "birthday": "01/02/03" }, "message": "Looks like HE made it ...\nCan't wait to see Petr on action again!\nIs ACRush competing with a different name BTW?", "created_time": "2011-02-06T21:31:32+0000" @@ -150,7 +182,9 @@ "id": "144568048938151_1238690", "from": { "name": "Sreesasanka Gunturi", - "id": "1581787658" + "id": "1581787658", + "gender": "male", + "birthday": "01/02/03" }, "message": "Guys, Is it happening to me or everyone. I cant see the problem set. Just that I did not qualify for round 2 does not mean I can never the problems(but download solutions!!!) for this round. Plzz post the problems if possible thanks...", "created_time": "2011-02-06T23:49:33+0000" @@ -159,7 +193,9 @@ "id": "144568048938151_1238852", "from": { "name": "Dario Lepre Battistin", - "id": "1704653001" + "id": "1704653001", + "gender": "male", + "birthday": "01/02/03" }, "message": "Se solo qualcuno mi insegnasse quest'arte ci proverei anch'io.", "created_time": "2011-02-07T00:14:19+0000" @@ -168,7 +204,9 @@ "id": "144568048938151_1239654", "from": { "name": "Ananya Mallik", - "id": "1560315625" + "id": "1560315625", + "gender": "male", + "birthday": "01/02/03" }, "message": "yeah acrush is rank 8 i suppose .\nhttp://forums.topcoder.com/;jsessionid=A066C5A0E84294BEB6ADA6FEC3D836E9?module=Thread&threadID=695777&start=800&mc=830#1320829", "created_time": "2011-02-07T02:16:57+0000" @@ -177,7 +215,9 @@ "id": "144568048938151_1241789", "from": { "name": "Mehmet Ali Karata\u015f", - "id": "100001241452656" + "id": "100001241452656", + "gender": "male", + "birthday": "01/02/03" }, "message": "hacked bay facebook xd !!! zaaaaaaaaaaaaa", "created_time": "2011-02-07T10:39:20+0000" @@ -186,7 +226,9 @@ "id": "144568048938151_1245190", "from": { "name": "Ankit Sablok", - "id": "1333740280" + "id": "1333740280", + "gender": "male", + "birthday": "01/02/03" }, "message": "who all r u giving t shirts", "created_time": "2011-02-07T21:08:37+0000" diff --git a/webapp/plugins/facebook/tests/testdata/1479111505 b/webapp/plugins/facebook/tests/testdata/1479111505 index 4b0922b99c..69781ad639 100644 --- a/webapp/plugins/facebook/tests/testdata/1479111505 +++ b/webapp/plugins/facebook/tests/testdata/1479111505 @@ -5,6 +5,7 @@ "last_name": "Chuchlik", "link": "http://www.facebook.com/harald.chuchlik", "gender": "male", + "birthday": "01/02/03", "locale": "de_DE", "updated_time": "2009-10-14T21:25:52+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/1560315625 b/webapp/plugins/facebook/tests/testdata/1560315625 index 5a6f195731..5fa8a2babf 100644 --- a/webapp/plugins/facebook/tests/testdata/1560315625 +++ b/webapp/plugins/facebook/tests/testdata/1560315625 @@ -6,6 +6,7 @@ "link": "https://www.facebook.com/ananya.mallik", "username": "ananya.mallik", "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2011-08-06T13:46:50+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/1704653001 b/webapp/plugins/facebook/tests/testdata/1704653001 index dea2a0cf40..bf539fa33a 100644 --- a/webapp/plugins/facebook/tests/testdata/1704653001 +++ b/webapp/plugins/facebook/tests/testdata/1704653001 @@ -6,6 +6,7 @@ "last_name": "Battistin", "link": "https://www.facebook.com/profile.php?id=1704653001", "gender": "male", + "birthday": "01/02/03", "locale": "en_GB", "updated_time": "2011-07-17T20:36:51+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/24404996 b/webapp/plugins/facebook/tests/testdata/24404996 index 2635aa6cc9..4464c5e3f1 100644 --- a/webapp/plugins/facebook/tests/testdata/24404996 +++ b/webapp/plugins/facebook/tests/testdata/24404996 @@ -5,6 +5,7 @@ "last_name": "Lawley", "link": "http://www.facebook.com/mamamusings", "gender": "female", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-06-27T06:05:01+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/500941232 b/webapp/plugins/facebook/tests/testdata/500941232 index de2a8de40f..024e808ba8 100644 --- a/webapp/plugins/facebook/tests/testdata/500941232 +++ b/webapp/plugins/facebook/tests/testdata/500941232 @@ -6,6 +6,8 @@ "link": "http://www.facebook.com/yaelkropsky", "locale": "en_US", "updated_time": "2010-05-28T04:38:31+0000", + "gender": "male", + "birthday": "01/02/03", "location": { "id": "109324835754083", "name": "Buffalo, New York" diff --git a/webapp/plugins/facebook/tests/testdata/501771984 b/webapp/plugins/facebook/tests/testdata/501771984 index b91acbf8fe..631adbeb37 100644 --- a/webapp/plugins/facebook/tests/testdata/501771984 +++ b/webapp/plugins/facebook/tests/testdata/501771984 @@ -7,6 +7,7 @@ "name": "Chris Moyer", "id": "501771984", "gender": "male", + "birthday": "01/02/03", "location": { "name": "Clarence, New York", "id": "108077575886746" diff --git a/webapp/plugins/facebook/tests/testdata/503315820 b/webapp/plugins/facebook/tests/testdata/503315820 index 1cc29258f5..0298e80ea0 100644 --- a/webapp/plugins/facebook/tests/testdata/503315820 +++ b/webapp/plugins/facebook/tests/testdata/503315820 @@ -39,7 +39,8 @@ "end_date": "2011-07" } ], - "gender": "male", + "gender": "male", + "birthday": "01/02/03", "website": "http://xn.pinkhamster.net/", "timezone": -7, "locale": "en_US", diff --git a/webapp/plugins/facebook/tests/testdata/510971571 b/webapp/plugins/facebook/tests/testdata/510971571 index 8f007be9c9..a1799499ec 100644 --- a/webapp/plugins/facebook/tests/testdata/510971571 +++ b/webapp/plugins/facebook/tests/testdata/510971571 @@ -5,7 +5,8 @@ "last_name": "Poole", "link": "https://www.facebook.com/p00le", "username": "p00le", - "gender": "male", + "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2011-06-21T09:59:32+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/533302090 b/webapp/plugins/facebook/tests/testdata/533302090 index af1d3e4ff9..564b573672 100644 --- a/webapp/plugins/facebook/tests/testdata/533302090 +++ b/webapp/plugins/facebook/tests/testdata/533302090 @@ -5,6 +5,7 @@ "last_name": "Lam", "link": "http://www.facebook.com/kennethhkg", "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-08-28T02:52:22+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/535362181 b/webapp/plugins/facebook/tests/testdata/535362181 index 98af484065..388a18974a 100644 --- a/webapp/plugins/facebook/tests/testdata/535362181 +++ b/webapp/plugins/facebook/tests/testdata/535362181 @@ -5,6 +5,7 @@ "last_name": "Settlemier", "link": "http://www.facebook.com/liteguru", "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-07-31T08:22:08+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/606837591 b/webapp/plugins/facebook/tests/testdata/606837591 index 18cd4fa823..c16040cf0c 100644 --- a/webapp/plugins/facebook/tests/testdata/606837591 +++ b/webapp/plugins/facebook/tests/testdata/606837591 @@ -1,6 +1,8 @@ { "id": "606837591", "name": "Gina Trapani", + "gender": "female", + "birthday": "01/02/03", "location": { "id": "110714572282163", "name": "San Diego, California" diff --git a/webapp/plugins/facebook/tests/testdata/606837591_153956564638648_comments b/webapp/plugins/facebook/tests/testdata/606837591_153956564638648_comments index 3b7038fb2d..ce7f942314 100644 --- a/webapp/plugins/facebook/tests/testdata/606837591_153956564638648_comments +++ b/webapp/plugins/facebook/tests/testdata/606837591_153956564638648_comments @@ -4,7 +4,9 @@ "id": "606837591_153956564638648_1538880", "from": { "name": "Neil Kelly", - "id": "653790081" + "id": "653790081", + "gender": "male", + "birthday": "01/02/03" }, "message": "I sincerely believe that Brittany is to Glee what Creed is to The Office ... One or two lines every episode but they're alway pure comedy gold. ", "created_time": "2010-09-28T18:06:11+0000" @@ -13,7 +15,9 @@ "id": "606837591_153956564638648_1538915", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "message": "I think my cat may be reading my diary.", "created_time": "2010-09-28T18:10:13+0000" @@ -22,7 +26,9 @@ "id": "606837591_153956564638648_1538938", "from": { "name": "Gina Trapani", - "id": "606837591" + "id": "606837591", + "gender": "female", + "birthday": "01/02/03" }, "message": "Did you know that dolphins are just gay sharks?", "created_time": "2010-09-28T18:15:19+0000" @@ -31,7 +37,9 @@ "id": "606837591_153956564638648_1538952", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "message": "Neil, I think that Brittney is better than her one-liners! They just haven't given her her shot yet. The monologue in season 1 where she ODed on cold medicine and confessed to making out with every boy, girl and janitor in school to achieve popularity was pretty well acted.", "created_time": "2010-09-28T18:17:11+0000" @@ -40,7 +48,9 @@ "id": "606837591_153956564638648_1538958", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "message": "Also: I find recipes confusing.", "created_time": "2010-09-28T18:17:51+0000" @@ -49,7 +59,9 @@ "id": "606837591_153956564638648_1538962", "from": { "name": "Neil Kelly", - "id": "653790081" + "id": "653790081", + "gender": "male", + "birthday": "01/02/03" }, "message": "My fave, although not spoken by her, is that she thinks the square root of four is rainbows. ", "created_time": "2010-09-28T18:18:18+0000" @@ -58,7 +70,9 @@ "id": "606837591_153956564638648_1538970", "from": { "name": "Neil Kelly", - "id": "653790081" + "id": "653790081", + "gender": "male", + "birthday": "01/02/03" }, "message": "Tonight may just be her breakout...", "created_time": "2010-09-28T18:19:28+0000" @@ -67,7 +81,9 @@ "id": "606837591_153956564638648_1538985", "from": { "name": "Gina Trapani", - "id": "606837591" + "id": "606837591", + "gender": "female", + "birthday": "01/02/03" }, "message": "When I pulled my hamstring I went to a misogynist.", "created_time": "2010-09-28T18:21:14+0000" @@ -76,7 +92,9 @@ "id": "606837591_153956564638648_1539016", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "message": "Sometimes I add a teaspoon of sand.", "created_time": "2010-09-28T18:24:43+0000" @@ -85,7 +103,9 @@ "id": "606837591_153956564638648_1539815", "from": { "name": "Yael Kropsky", - "id": "500941232" + "id": "500941232", + "gender": "male", + "birthday": "01/02/03" }, "message": "I kind of wanted to touch *her* boobs.", "created_time": "2010-09-28T20:14:01+0000" @@ -94,7 +114,9 @@ "id": "606837591_153956564638648_1539847", "from": { "name": "Yael Kropsky", - "id": "500941232" + "id": "500941232", + "gender": "male", + "birthday": "01/02/03" }, "message": "Ooh, and: Sometimes I forget my middle name.", "created_time": "2010-09-28T20:18:16+0000" @@ -103,7 +125,9 @@ "id": "606837591_153956564638648_1541555", "from": { "name": "Neil Kelly", - "id": "653790081" + "id": "653790081", + "gender": "male", + "birthday": "01/02/03" }, "message": "Finn can fly?", "created_time": "2010-09-29T01:45:55+0000" @@ -112,7 +136,9 @@ "id": "606837591_153956564638648_1544695", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "message": "Very disappointed by this episode. Starting to fear that \"Glee\" may be another \"Heroes.\"", "created_time": "2010-09-29T13:42:02+0000" @@ -121,7 +147,9 @@ "id": "606837591_153956564638648_1545655", "from": { "name": "Gina Trapani", - "id": "606837591" + "id": "606837591", + "gender": "female", + "birthday": "01/02/03" }, "message": "Really, Mitch? Why disappointed? Slave 4 U and One More Time were awesome. Stronger and Toxic could have been better. But overall I thought it was pretty entertaining.", "created_time": "2010-09-29T16:02:30+0000" @@ -130,7 +158,9 @@ "id": "606837591_153956564638648_1545690", "from": { "name": "Neil Kelly", - "id": "653790081" + "id": "653790081", + "gender": "male", + "birthday": "01/02/03" }, "message": "It was very entertaining but felt forced - like they jammed Britney in instead of working the music into the plot. The Madonna episode felt the same way, and to a lesser extent, Lady Gaga. Maybe stop with the themes and just use the right song for the plot?", "created_time": "2010-09-29T16:08:30+0000" @@ -139,7 +169,9 @@ "id": "606837591_153956564638648_1545758", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "message": "Wait, Glee has a plot?", "created_time": "2010-09-29T16:17:08+0000" @@ -148,7 +180,9 @@ "id": "606837591_153956564638648_1545886", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "message": "Also, may I say I appreciated the internet fan service in this one. It was such a no-brainer for them to have someone say \"is this real life?\" but they did it, and I frickin' love them for that.", "created_time": "2010-09-29T16:32:21+0000" @@ -157,7 +191,9 @@ "id": "606837591_153956564638648_1546020", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "message": "I'm not the target demographic for this. I don't like Britney Spears music. I barely know it.\n\nThe episode still had its moments, though. \"Is this real life\" was awesome. She had the facial expression right too. And Sue Sylvester and the principal were, of course, fantastic. Couple of great lines there.", "created_time": "2010-09-29T16:51:27+0000" @@ -166,7 +202,9 @@ "id": "606837591_153956564638648_1552762", "from": { "name": "Yael Kropsky", - "id": "500941232" + "id": "500941232", + "gender": "male", + "birthday": "01/02/03" }, "message": "I think Sue stole the episode (again) with Gloria Allred being her lawyer and, \"you wear more vests than the cast of Blossom.\" My main issue with Glee is the voice correction/overproduction of the songs. And not enough leotards and legwarmers. \n\nCheck out http://www.youtube.com/watch?v=W3aCAMRjuBM to see how it's done.", "created_time": "2010-09-30T14:27:39+0000" diff --git a/webapp/plugins/facebook/tests/testdata/606837591_feed b/webapp/plugins/facebook/tests/testdata/606837591_feed index c4ece7b970..480ba8386f 100644 --- a/webapp/plugins/facebook/tests/testdata/606837591_feed +++ b/webapp/plugins/facebook/tests/testdata/606837591_feed @@ -100,7 +100,9 @@ "created_time": "2013-11-26T06:34:29+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -112,7 +114,9 @@ "created_time": "2013-11-26T06:35:25+0000", "from": { "name": "Jeffrey McManus", - "id": "691270740" + "id": "691270740", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -124,7 +128,9 @@ "created_time": "2013-11-26T06:35:27+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -136,7 +142,9 @@ "created_time": "2013-11-26T06:35:28+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -148,7 +156,9 @@ "created_time": "2013-11-26T06:35:29+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -160,7 +170,9 @@ "created_time": "2013-11-26T06:35:29+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -172,7 +184,9 @@ "created_time": "2013-11-26T06:35:30+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -184,7 +198,9 @@ "created_time": "2013-11-26T06:35:30+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -196,7 +212,9 @@ "created_time": "2013-11-26T06:35:31+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -208,7 +226,9 @@ "created_time": "2013-11-26T06:35:34+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -220,7 +240,9 @@ "created_time": "2013-11-26T06:35:37+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -232,7 +254,9 @@ "created_time": "2013-11-26T06:35:40+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -244,7 +268,9 @@ "created_time": "2013-11-26T06:35:43+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -256,7 +282,9 @@ "created_time": "2013-11-26T06:35:48+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -268,7 +296,9 @@ "created_time": "2013-11-26T06:35:54+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -280,7 +310,9 @@ "created_time": "2013-11-26T06:35:57+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -292,7 +324,9 @@ "created_time": "2013-11-26T06:35:59+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -304,7 +338,9 @@ "created_time": "2013-11-26T06:36:02+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -316,7 +352,9 @@ "created_time": "2013-11-26T06:36:04+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -328,7 +366,9 @@ "created_time": "2013-11-26T06:36:21+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -340,7 +380,9 @@ "created_time": "2013-11-26T06:36:23+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -352,7 +394,9 @@ "created_time": "2013-11-26T06:36:23+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -364,7 +408,9 @@ "created_time": "2013-11-26T06:36:23+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -376,7 +422,9 @@ "created_time": "2013-11-26T06:36:24+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -388,7 +436,9 @@ "created_time": "2013-11-26T06:36:24+0000", "from": { "name": "Chris Moyer", - "id": "501771984" + "id": "501771984", + "gender": "male", + "birthday": "01/02/03" }, "user_likes": false, "can_remove": true, @@ -503,7 +553,9 @@ "created_time": "2010-09-21T19:34:51+0000", "from": { "name": "Marcy Bailey Carrico", - "id": "1034402187" + "id": "1034402187", + "gender": "female", + "birthday": "01/02/03" }, "id": "606837591_435724732591_13699089" }, @@ -512,7 +564,9 @@ "created_time": "2010-09-21T20:38:25+0000", "from": { "name": "Gina Trapani", - "id": "606837591" + "id": "606837591", + "gender": "female", + "birthday": "01/02/03" }, "id": "606837591_435724732591_13699456" } @@ -639,7 +693,9 @@ "created_time": "2010-09-17T02:51:52+0000", "from": { "name": "Dan Tentler", - "id": "501805642" + "id": "501805642", + "gender": "male", + "birthday": "01/02/03" }, "id": "606837591_128550830526770_817240" }, @@ -648,7 +704,9 @@ "created_time": "2010-09-17T02:54:11+0000", "from": { "name": "Zack Murtha", - "id": "1316858804" + "id": "1316858804", + "gender": "male", + "birthday": "01/02/03" }, "id": "606837591_128550830526770_817252" }, @@ -657,7 +715,9 @@ "created_time": "2010-09-17T08:19:42+0000", "from": { "name": "Marcy Bailey Carrico", - "id": "1034402187" + "id": "1034402187", + "gender": "female", + "birthday": "01/02/03" }, "id": "606837591_128550830526770_818549" } @@ -742,7 +802,9 @@ "created_time": "2010-08-29T17:03:42+0000", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "id": "606837591_427489887591_13559353" }, @@ -751,7 +813,9 @@ "created_time": "2010-09-20T03:15:39+0000", "from": { "name": "Elizabeth Lawley", - "id": "24404996" + "id": "24404996", + "gender": "female", + "birthday": "01/02/03" }, "id": "606837591_427489887591_13687827" } @@ -882,7 +946,9 @@ "created_time": "2010-08-11T22:35:51+0000", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "id": "606837591_141642905870455_1143876" }, @@ -891,7 +957,9 @@ "created_time": "2010-08-11T23:49:45+0000", "from": { "name": "Marcy Bailey Carrico", - "id": "1034402187" + "id": "1034402187", + "gender": "female", + "birthday": "01/02/03" }, "id": "606837591_141642905870455_1144238" } diff --git a/webapp/plugins/facebook/tests/testdata/606837591_friends b/webapp/plugins/facebook/tests/testdata/606837591_friends index 6a86faaa31..6a998dfaff 100644 --- a/webapp/plugins/facebook/tests/testdata/606837591_friends +++ b/webapp/plugins/facebook/tests/testdata/606837591_friends @@ -2,7 +2,9 @@ "data": [ { "name": "Adrian Valera", - "id": "833828624" + "id": "833828624", + "gender": "male", + "birthday": "01/02/03" } ] } diff --git a/webapp/plugins/facebook/tests/testdata/606837591_posts b/webapp/plugins/facebook/tests/testdata/606837591_posts index 3b238f456a..8a4ea3cbcf 100644 --- a/webapp/plugins/facebook/tests/testdata/606837591_posts +++ b/webapp/plugins/facebook/tests/testdata/606837591_posts @@ -91,7 +91,9 @@ "id": "606837591_153956564638648_1546020", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "message": "I'm not the target demographic for this. I don't like Britney Spears music. I barely know it.\n\nThe episode still had its moments, though. \"Is this real life\" was awesome. She had the facial expression right too. And Sue Sylvester and the principal were, of course, fantastic. Couple of great lines there.", "created_time": "2010-09-29T16:51:27+0000" @@ -100,7 +102,9 @@ "id": "606837591_153956564638648_1552762", "from": { "name": "Yael Kropsky", - "id": "500941232" + "id": "500941232", + "gender": "male", + "birthday": "01/02/03" }, "message": "I think Sue stole the episode (again) with Gloria Allred being her lawyer and, \"you wear more vests than the cast of Blossom.\" My main issue with Glee is the voice correction/overproduction of the songs. And not enough leotards and legwarmers. \n\nCheck out http://www.youtube.com/watch?v=W3aCAMRjuBM to see how it's done.", "created_time": "2010-09-30T14:27:39+0000" @@ -215,7 +219,9 @@ "id": "606837591_435724732591_13699089", "from": { "name": "Marcy Bailey Carrico", - "id": "1034402187" + "id": "1034402187", + "gender": "female", + "birthday": "01/02/03" }, "message": "seriously? Can we see it online? (okay, and 6 hours after the fact...) When are you going to be on national news where we can see you? I'll even turn my tv channel off of Fox News to see you ;)", "created_time": "2010-09-21T19:34:51+0000" @@ -224,7 +230,9 @@ "id": "606837591_435724732591_13699456", "from": { "name": "Gina Trapani", - "id": "606837591" + "id": "606837591", + "gender": "female", + "birthday": "01/02/03" }, "message": "Oh I wasn't on! I was there to see a friend of mine do her last segment before she leaves for SF. Just kind of checking things out. Photo in the (very small, TV looks are deceiving) studio:\nhttp://www.flickr.com/photos/ginatrapani/5011175441/", "created_time": "2010-09-21T20:38:25+0000" @@ -349,7 +357,9 @@ "id": "606837591_128550830526770_817240", "from": { "name": "Dan Tentler", - "id": "501805642" + "id": "501805642", + "gender": "male", + "birthday": "01/02/03" }, "message": "depends on what channel and what the context is. \n\nI'd go on G4 probably, penn and tellers bullshit, absolutely.", "created_time": "2010-09-17T02:51:52+0000" @@ -358,7 +368,9 @@ "id": "606837591_128550830526770_817252", "from": { "name": "Zack Murtha", - "id": "1316858804" + "id": "1316858804", + "gender": "male", + "birthday": "01/02/03" }, "message": "I'm always hoping for the call to Max after Dark. Never seems to come though.", "created_time": "2010-09-17T02:54:11+0000" @@ -367,7 +379,9 @@ "id": "606837591_128550830526770_818549", "from": { "name": "Marcy Bailey Carrico", - "id": "1034402187" + "id": "1034402187", + "gender": "female", + "birthday": "01/02/03" }, "message": "I want to see you on tv. When and what channel?", "created_time": "2010-09-17T08:19:42+0000" @@ -406,7 +420,9 @@ "id": "606837591_151660894852759_1414665", "from": { "name": "Kevin Purdy", - "id": "677588391" + "id": "677588391", + "gender": "male", + "birthday": "01/02/03" }, "message": "But, Gina! He/she has something that they \"know your readers will find interesting\"!", "created_time": "2010-09-04T18:53:23+0000" @@ -454,7 +470,9 @@ "id": "606837591_427489887591_13559353", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "message": "Getting a fit bit? Let us know what you think please.", "created_time": "2010-08-29T17:03:42+0000" @@ -463,7 +481,9 @@ "id": "606837591_427489887591_13687827", "from": { "name": "Elizabeth Lawley", - "id": "24404996" + "id": "24404996", + "gender": "female", + "birthday": "01/02/03" }, "message": "/want", "created_time": "2010-09-20T03:15:39+0000" @@ -595,7 +615,9 @@ "id": "606837591_141642905870455_1143876", "from": { "name": "Mitch Wagner", - "id": "697015835" + "id": "697015835", + "gender": "male", + "birthday": "01/02/03" }, "message": "Delicious, delicious culinary terrorism. ", "created_time": "2010-08-11T22:35:51+0000" @@ -604,7 +626,9 @@ "id": "606837591_141642905870455_1144238", "from": { "name": "Marcy Bailey Carrico", - "id": "1034402187" + "id": "1034402187", + "gender": "female", + "birthday": "01/02/03" }, "message": "I have only one thing for you: www.thisiswhyyourefat.com <3", "created_time": "2010-08-11T23:49:45+0000" @@ -643,7 +667,9 @@ "id": "606837591_148686895142561_1335723", "from": { "name": "Kevin Purdy", - "id": "677588391" + "id": "677588391", + "gender": "male", + "birthday": "01/02/03" }, "message": "Happens all the time. Now just don't go too deep into their Facebook profiles.", "created_time": "2010-08-07T11:21:17+0000" diff --git a/webapp/plugins/facebook/tests/testdata/625158327 b/webapp/plugins/facebook/tests/testdata/625158327 index 75c28f8dee..5370c68d5e 100644 --- a/webapp/plugins/facebook/tests/testdata/625158327 +++ b/webapp/plugins/facebook/tests/testdata/625158327 @@ -6,6 +6,7 @@ "last_name": "Akmol", "link": "http://www.facebook.com/rahulnakmol", "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-07-23T13:46:30+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/641265671 b/webapp/plugins/facebook/tests/testdata/641265671 index d3b7987562..8776005832 100644 --- a/webapp/plugins/facebook/tests/testdata/641265671 +++ b/webapp/plugins/facebook/tests/testdata/641265671 @@ -5,7 +5,8 @@ "last_name": "Pike", "link": "https://www.facebook.com/tigger.pike", "username": "tigger.pike", - "gender": "female", + "gender": "female", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2011-08-26T18:55:57+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/653790081 b/webapp/plugins/facebook/tests/testdata/653790081 index 0ddeaa0fb1..d86d564260 100644 --- a/webapp/plugins/facebook/tests/testdata/653790081 +++ b/webapp/plugins/facebook/tests/testdata/653790081 @@ -5,5 +5,8 @@ "location": { "id": "109324835754083", "name": "Buffalo, New York" - } + }, + "gender": "male", + "birthday": "01/02/03" + } diff --git a/webapp/plugins/facebook/tests/testdata/677588391 b/webapp/plugins/facebook/tests/testdata/677588391 index 4ffc0671e2..e82b6808a4 100644 --- a/webapp/plugins/facebook/tests/testdata/677588391 +++ b/webapp/plugins/facebook/tests/testdata/677588391 @@ -91,6 +91,7 @@ } ], "gender": "male", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2010-07-25T22:53:13+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/682523675 b/webapp/plugins/facebook/tests/testdata/682523675 index f8847f9e47..f6a3c3227e 100644 --- a/webapp/plugins/facebook/tests/testdata/682523675 +++ b/webapp/plugins/facebook/tests/testdata/682523675 @@ -5,7 +5,8 @@ "last_name": "Linford", "link": "https://www.facebook.com/poppynicole", "username": "poppynicole", - "gender": "female", + "gender": "female", + "birthday": "01/02/03", "locale": "en_US", "updated_time": "2011-03-28T17:48:49+0000", "type": "user" diff --git a/webapp/plugins/facebook/tests/testdata/691270740 b/webapp/plugins/facebook/tests/testdata/691270740 index 238981487a..8e6013eb0f 100644 --- a/webapp/plugins/facebook/tests/testdata/691270740 +++ b/webapp/plugins/facebook/tests/testdata/691270740 @@ -1,5 +1,7 @@ { "id": "691270740", - "name": "Jeffrey McManus", + "name": "Jeffrey McManus", + "gender": "male", + "birthday": "01/02/03", "type": "user" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/729597743 b/webapp/plugins/facebook/tests/testdata/729597743 index 8d1b4c7970..afe519f251 100644 --- a/webapp/plugins/facebook/tests/testdata/729597743 +++ b/webapp/plugins/facebook/tests/testdata/729597743 @@ -1,5 +1,7 @@ { "id": "729597743", "name": "Mark Linford", + "gender": "male", + "birthday": "01/02/03", "about": "Old school proto-geek winding his way through this life" } diff --git a/webapp/plugins/facebook/tests/testdata/729597743_feed b/webapp/plugins/facebook/tests/testdata/729597743_feed index d17a91cdf8..2739b5e688 100644 --- a/webapp/plugins/facebook/tests/testdata/729597743_feed +++ b/webapp/plugins/facebook/tests/testdata/729597743_feed @@ -11,7 +11,9 @@ "type": "video", "from": { "id": "729597743", - "name": "Mark Linford" + "name": "Mark Linford", + "gender": "male", + "birthday": "01/02/03" }, "source": "http://www.youtube.com/v/DC1g_Aq3dUc?feature=autoshare&version=3&autohide=1&autoplay=1", "caption": "Liked on www.youtube.com", diff --git a/webapp/plugins/facebook/tests/testdata/729597743_friends b/webapp/plugins/facebook/tests/testdata/729597743_friends index dd28d742f1..4d55f34473 100644 --- a/webapp/plugins/facebook/tests/testdata/729597743_friends +++ b/webapp/plugins/facebook/tests/testdata/729597743_friends @@ -2,7 +2,9 @@ "data": [ { "name": "Poppy Linford", - "id": "682523675" + "id": "682523675", + "gender": "female", + "birthday": "01/02/03" } ] } diff --git a/webapp/plugins/facebook/tests/testdata/729597743_posts b/webapp/plugins/facebook/tests/testdata/729597743_posts index 55d78ebcea..8d6bd94a0f 100644 --- a/webapp/plugins/facebook/tests/testdata/729597743_posts +++ b/webapp/plugins/facebook/tests/testdata/729597743_posts @@ -14,7 +14,9 @@ "id": "729597743_10150328374252744", "from": { "id": "729597743", - "name": "Mark Linford" + "name": "Mark Linford", + "gender": "male", + "birthday": "01/02/03" }, "privacy": { "description": "Friends Only", diff --git a/webapp/plugins/facebook/tests/testdata/729597743_posts-limit=25-until=1313604329-since=0 b/webapp/plugins/facebook/tests/testdata/729597743_posts-limit=25-until=1313604329-since=0 index 667d2fb2ab..d4206e3b5b 100644 --- a/webapp/plugins/facebook/tests/testdata/729597743_posts-limit=25-until=1313604329-since=0 +++ b/webapp/plugins/facebook/tests/testdata/729597743_posts-limit=25-until=1313604329-since=0 @@ -33,7 +33,7 @@ "created_time": "2011-09-06T20:00:43+0000", "updated_time": "2011-09-06T20:00:43+0000", "comments": { - "count": 0 + "count": 0 } } ] diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_10151728144886356_comments-limit=25-after=Njk= b/webapp/plugins/facebook/tests/testdata/7568536355_10151728144886356_comments-limit=25-after=Njk= index c1f598be1e..9633108b4c 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_10151728144886356_comments-limit=25-after=Njk= +++ b/webapp/plugins/facebook/tests/testdata/7568536355_10151728144886356_comments-limit=25-after=Njk= @@ -4,7 +4,9 @@ "id": "437900891355_28175606", "from": { "name": "Justin Wall", - "id": "333500103" + "id": "333500103", + "gender": "male", + "birthday": "01/02/03" }, "message": "RAM is free these days: http://downloadmoreram.com", "can_remove": false, @@ -16,7 +18,9 @@ "id": "437900891355_28175601", "from": { "name": "Jessica A Thomas", - "id": "604622899" + "id": "604622899", + "gender": "female", + "birthday": "01/02/03" }, "message": "ram is like cowbell, you always need more", "can_remove": false, @@ -28,7 +32,9 @@ "id": "437900891355_28175679", "from": { "name": "Howard Davies", - "id": "100000073694609" + "id": "100000073694609", + "gender": "male", + "birthday": "01/02/03" }, "message": "If a person is still using Windows 32bit they are wasting their money on more RAM, anyways. It will only use 3+gigs of RAM only.", "can_remove": false, @@ -40,7 +46,8 @@ "id": "437900891355_28177015", "from": { "name": "Swapnil Reloaded", - "id": "100002389208464" + "id": "10000238920, + "birthday": "01/02/03" }, "message": "Wait for DDR4 to come out ;)", "can_remove": false, @@ -52,7 +59,9 @@ "id": "437900891355_28176730", "from": { "name": "Wade Stubblefield", - "id": "668312109" + "id": "668312109", + "gender": "female", + "birthday": "01/02/03" }, "message": "I run StarCraft II and Diablo III just fine on 4 GB DDR3. If I upgraded that, I would replace it all with 4 GB DDR3 with a higher frequency.\n\nThat's important but most people have no idea about it. Higher megahertz RAM, people.", "can_remove": false, @@ -64,7 +73,9 @@ "id": "437900891355_28176684", "from": { "name": "Robert Williams", - "id": "100002380802361" + "id": "100002380802361", + "gender": "male", + "birthday": "01/02/03" }, "message": "or you know if youre still running a dual core intel pentium, it might be time to swap that out", "can_remove": false, @@ -76,7 +87,9 @@ "id": "437900891355_28176537", "from": { "name": "Mart Marandi", - "id": "100001110528726" + "id": "100001110528726", + "gender": "female", + "birthday": "01/02/03" }, "message": "Ram or Lamb?", "can_remove": false, @@ -88,7 +101,9 @@ "id": "437900891355_28176335", "from": { "name": "Maximilian Bocchine", - "id": "1198561283" + "id": "1198561283", + "gender": "male", + "birthday": "01/02/03" }, "message": "This article was clearly not meant for me.\n/installed 16 gigs & doing fine now", "can_remove": false, @@ -100,7 +115,9 @@ "id": "437900891355_28176303", "from": { "name": "Jean-Français Carmer", - "id": "796205273" + "id": "796205273", + "gender": "male", + "birthday": "01/02/03" }, "message": "Pier-Olivier Leclair :)", "message_tags": [ @@ -121,7 +138,9 @@ "id": "437900891355_28176127", "from": { "name": "Bro PanDa", - "id": "100000173096804" + "id": "100000173096804", + "gender": "male", + "birthday": "01/02/03" }, "message": "Well if anyone wants to add more to RAM, go to \"Computer\" and add more RAM... O_O", "can_remove": false, @@ -133,7 +152,9 @@ "id": "437900891355_28175987", "from": { "name": "Jan Andrew Bloxham", - "id": "555279771" + "id": "555279771", + "gender": "male", + "birthday": "01/02/03" }, "message": "It hasn't been the case for many years.", "can_remove": false, @@ -145,7 +166,9 @@ "id": "437900891355_28175901", "from": { "name": "Roman Nahal", - "id": "1023357430" + "id": "1023357430", + "gender": "male", + "birthday": "01/02/03" }, "message": "Not unless you harness the power through software via a USB. www.dramdisk.com", "can_remove": false, @@ -157,7 +180,9 @@ "id": "437900891355_28175841", "from": { "name": "Sylwen Cabose", - "id": "100000618350257" + "id": "100000618350257", + "gender": "female", + "birthday": "01/02/03" }, "message": "The price of ram has doubled in the pass year, why is that?", "can_remove": false, @@ -169,7 +194,9 @@ "id": "437900891355_28175832", "from": { "name": "Sif Lord", - "id": "100000600778454" + "id": "100000600778454", + "gender": "male", + "birthday": "01/02/03" }, "message": "Pushing the Limits of Windows: Physical Memory\nhttp://blogs.technet.com/b/markrussinovich/archive/2008/07/21/3092070.aspx", "can_remove": false, @@ -181,7 +208,9 @@ "id": "437900891355_28175613", "from": { "name": "Alexander Hoke", - "id": "100000656926595" + "id": "100000656926595", + "gender": "male", + "birthday": "01/02/03" }, "message": "Linus! :D", "can_remove": false, @@ -193,7 +222,9 @@ "id": "437900891355_28176685", "from": { "name": "Mat Babyak", - "id": "100002389438263" + "id": "100002389438263", + "gender": "male", + "birthday": "01/02/03" }, "message": "6gb is more than enough for even a gaming rig, more is overkill", "can_remove": false, @@ -205,7 +236,9 @@ "id": "437900891355_28175934", "from": { "name": "Dapper Dab", - "id": "100003025575055" + "id": "100003025575055", + "gender": "male", + "birthday": "01/02/03" }, "message": "While he has some good points... he's leaving out some good things as well...", "can_remove": false, @@ -217,7 +250,9 @@ "id": "437900891355_28175645", "from": { "name": "Jonathon Wisnoski", - "id": "576534241" + "id": "576534241", + "gender": "male", + "birthday": "01/02/03" }, "message": "Well now with computers coming with 8+ gigs, I cannot think that you are likely to even used all the ram you have 99% of the time.", "can_remove": false, @@ -229,7 +264,9 @@ "id": "437900891355_28176037", "from": { "name": "Mark Quitoriano", - "id": "734732932" + "id": "734732932", + "gender": "male", + "birthday": "01/02/03" }, "message": "if only i can put 32GB of ram", "can_remove": false, @@ -241,7 +278,9 @@ "id": "437900891355_28176149", "from": { "name": "Ian Graves", - "id": "1549913136" + "id": "1549913136", + "gender": "male", + "birthday": "01/02/03" }, "message": "I had 32gb in my machine and most of it wasn't even used", "can_remove": false, diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_437612826355_comments b/webapp/plugins/facebook/tests/testdata/7568536355_437612826355_comments index c5ba0c4cef..9240cfef12 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_437612826355_comments +++ b/webapp/plugins/facebook/tests/testdata/7568536355_437612826355_comments @@ -4,7 +4,9 @@ "id": "7568536355_437612826355_13760381", "from": { "name": "Danyelle Davis", - "id": "672175726" + "id": "672175726", + "gender": "male", + "birthday": "01/02/03" }, "message": "how about those single white female heels.. that should work like a center punch ", "created_time": "2010-10-01T17:09:21+0000" @@ -13,7 +15,9 @@ "id": "7568536355_437612826355_13760449", "from": { "name": "Lesley Arak", - "id": "711306615" + "id": "711306615", + "gender": "male", + "birthday": "01/02/03" }, "message": "Mythbusters did a whole bit on this. If you wait for the car to fill with enough water, you can open the door and swim away.", "created_time": "2010-10-01T17:17:15+0000" @@ -22,7 +26,9 @@ "id": "7568536355_437612826355_13760461", "from": { "name": "David Perreko", - "id": "1016922173" + "id": "1016922173", + "gender": "male", + "birthday": "01/02/03" }, "message": "farting lots will pump out water and make the car go up", "created_time": "2010-10-01T17:18:42+0000" @@ -31,7 +37,9 @@ "id": "7568536355_437612826355_13760488", "from": { "name": "H\u00e5var Ingmund Henriksen \u272b", - "id": "549455565" + "id": "549455565", + "gender": "male", + "birthday": "01/02/03" }, "message": "I've heard that it also is possible/easier to kick out the windshield of the car (the front window).", "created_time": "2010-10-01T17:24:56+0000" @@ -40,7 +48,9 @@ "id": "7568536355_437612826355_13760496", "from": { "name": "T.j. Fogarty", - "id": "1278735591" + "id": "1278735591", + "gender": "male", + "birthday": "01/02/03" }, "message": "I felt it wasn't realistic enough. I wanted to see them go 40mph into a lake and then try get out.", "created_time": "2010-10-01T17:27:49+0000" @@ -49,7 +59,9 @@ "id": "7568536355_437612826355_13760507", "from": { "name": "Ernst Sullivan", - "id": "624306676" + "id": "624306676", + "gender": "male", + "birthday": "01/02/03" }, "message": "Indeed T.J", "created_time": "2010-10-01T17:31:54+0000" @@ -58,7 +70,9 @@ "id": "7568536355_437612826355_13760526", "from": { "name": "Jacky Chan", - "id": "797338851" + "id": "797338851", + "gender": "male", + "birthday": "01/02/03" }, "message": "just ask the mythbusters instead..", "created_time": "2010-10-01T17:37:01+0000" @@ -67,7 +81,9 @@ "id": "7568536355_437612826355_13760595", "from": { "name": "David Omnomnomagon Norman", - "id": "657290652" + "id": "657290652", + "gender": "male", + "birthday": "01/02/03" }, "message": "Top Gear did this aages ago. Must be a common thing shows get asked.", "created_time": "2010-10-01T17:50:09+0000" @@ -76,7 +92,9 @@ "id": "7568536355_437612826355_13760735", "from": { "name": "Allan Clark", - "id": "100000770145448" + "id": "100000770145448", + "gender": "male", + "birthday": "01/02/03" }, "message": "A spring-loaded machinist's center punch works beautifully. It's the size of a fat ballpoint pen, and should cost less than $20. Useless against the windscreen, but works a treat on the tempered side glass. Hyperventilate, hold your breath, pop the glass, wait a bit for the water to fill the cabin, and swim out. Leave the gun, and the canolis.", "created_time": "2010-10-01T18:16:57+0000" @@ -85,7 +103,9 @@ "id": "7568536355_437612826355_13760758", "from": { "name": "Peggy Jantzen", - "id": "1602455008" + "id": "1602455008", + "gender": "male", + "birthday": "01/02/03" }, "message": "The Mythbusters method is on the same link. Just scroll down.", "created_time": "2010-10-01T18:22:22+0000" @@ -94,7 +114,9 @@ "id": "7568536355_437612826355_13760775", "from": { "name": "Edward Palmer", - "id": "673353389" + "id": "673353389", + "gender": "male", + "birthday": "01/02/03" }, "message": "lol..considering recent current events..How about changing #1 to \"Drop Cell Phone, not ask your daughter to call the Ins. Company.\"", "created_time": "2010-10-01T18:28:32+0000" @@ -103,7 +125,9 @@ "id": "7568536355_437612826355_13761150", "from": { "name": "Ryan Evan Zupfer", - "id": "1362247319" + "id": "1362247319", + "gender": "male", + "birthday": "01/02/03" }, "message": "saw this on mythbusters", "created_time": "2010-10-01T19:35:55+0000" @@ -112,7 +136,9 @@ "id": "7568536355_437612826355_13762366", "from": { "name": "Alberto Alonso", - "id": "616400947" + "id": "616400947", + "gender": "male", + "birthday": "01/02/03" }, "message": "I love solutions to common everyday problems and hiccups, like trying to get out of a sinking car...", "created_time": "2010-10-01T23:50:23+0000" @@ -121,7 +147,9 @@ "id": "7568536355_437612826355_13763318", "from": { "name": "Alexander Schelasin", - "id": "564811788" + "id": "564811788", + "gender": "male", + "birthday": "01/02/03" }, "message": "Don't drive into a body of water in the first place! Jeeze.", "created_time": "2010-10-02T03:41:06+0000" diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_comments b/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_comments index 9a770ef1c5..df4899bd70 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_comments +++ b/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_comments @@ -4,7 +4,9 @@ "id": "7568536355_437894121355_13765328", "from": { "name": "Matthew Fleisher", - "id": "1036552729" + "id": "1036552729", + "gender": "male", + "birthday": "01/02/03" }, "message": "The only way to make an iphone useful?", "created_time": "2010-10-02T16:13:09+0000" @@ -13,7 +15,9 @@ "id": "7568536355_437894121355_13765337", "from": { "name": "Harsh Wardhan Gunthey", - "id": "1137640742" + "id": "1137640742", + "gender": "male", + "birthday": "01/02/03" }, "message": "@mattews \n\nso true..!", "created_time": "2010-10-02T16:15:51+0000" @@ -22,7 +26,9 @@ "id": "7568536355_437894121355_13765492", "from": { "name": "Harald Chuchlik", - "id": "1479111505" + "id": "1479111505", + "gender": "male", + "birthday": "01/02/03" }, "message": "Troll somewhere else", "created_time": "2010-10-02T16:44:28+0000" @@ -31,7 +37,9 @@ "id": "7568536355_437894121355_13765969", "from": { "name": "Chad Settlemier", - "id": "535362181" + "id": "535362181", + "gender": "male", + "birthday": "01/02/03" }, "message": "What about iPhone 4 top ten Jailbroken apps?", "created_time": "2010-10-02T18:10:52+0000" diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_likes b/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_likes index 2ee656a050..2830e531a2 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_likes +++ b/webapp/plugins/facebook/tests/testdata/7568536355_437894121355_likes @@ -2,11 +2,15 @@ "data": [ { "id": "100002586603826", - "name": "Sade Tolgahan" + "name": "Sade Tolgahan", + "gender": "female", + "birthday": "01/02/03" }, { "id": "100000931059876", - "name": "Kosso Thierry" + "name": "Kosso Thierry", + "gender": "male", + "birthday": "01/02/03" }, ], "paging": { diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_437900891355_comments b/webapp/plugins/facebook/tests/testdata/7568536355_437900891355_comments index 118c65da12..67f42d20b7 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_437900891355_comments +++ b/webapp/plugins/facebook/tests/testdata/7568536355_437900891355_comments @@ -4,7 +4,9 @@ "id": "7568536355_437900891355_13765434", "from": { "name": "Kevin Hulett", - "id": "1102363732" + "id": "1102363732", + "gender": "male", + "birthday": "01/02/03" }, "message": "SBRotator and LSRotator are both great, allowing you to use your iPhone/iPod in landscape all the time! ", "created_time": "2010-10-02T16:33:35+0000" @@ -13,7 +15,9 @@ "id": "7568536355_437900891355_13765453", "from": { "name": "Alex Smith", - "id": "667992894" + "id": "667992894", + "gender": "male", + "birthday": "01/02/03" }, "message": "Been wondering I this is what I want to do?", "created_time": "2010-10-02T16:37:14+0000" @@ -22,7 +26,9 @@ "id": "7568536355_437900891355_13765543", "from": { "name": "Mohsin Saleem", - "id": "1297248120" + "id": "1297248120", + "gender": "male", + "birthday": "01/02/03" }, "message": "Blackrain was the only jailbreak which was all time best.", "created_time": "2010-10-02T16:54:03+0000" @@ -31,7 +37,9 @@ "id": "7568536355_437900891355_13765693", "from": { "name": "Jocelyn Martina Hood", - "id": "535531069" + "id": "535531069", + "gender": "female", + "birthday": "01/02/03" }, "message": "I admit it. My phone is not jailbroken. But that's only because I'm too chicken. What are some benefits?... Drawbacks?", "created_time": "2010-10-02T17:20:44+0000" @@ -40,7 +48,9 @@ "id": "7568536355_437900891355_13765986", "from": { "name": "Bj Melo", - "id": "1402383719" + "id": "1402383719", + "gender": "male", + "birthday": "01/02/03" }, "message": "Can't believe you mention My3G, but not the superior 3G Unrestrictor.", "created_time": "2010-10-02T18:13:16+0000" @@ -49,7 +59,9 @@ "id": "7568536355_437900891355_13766497", "from": { "name": "Max Kurmayev", - "id": "1605905218" + "id": "1605905218", + "gender": "male", + "birthday": "01/02/03" }, "message": "I think the LockInfo has more options than Element LockScreen Cydget by teehan and lax and it's much faster.\n\nCheck out this LockInfo install and tutorial\n\nhttp://tinyurl.com/LockInfo-iPhoneTechie", "created_time": "2010-10-02T20:05:18+0000" @@ -58,7 +70,9 @@ "id": "7568536355_437900891355_13767800", "from": { "name": "Adrian Valera", - "id": "833828624" + "id": "833828624", + "gender": "male", + "birthday": "01/02/03" }, "message": "Cool Top 10", "created_time": "2010-10-03T01:58:01+0000" @@ -67,7 +81,9 @@ "id": "7568536355_437900891355_13768553", "from": { "name": "Rahul Narasimha Akmol", - "id": "625158327" + "id": "625158327", + "gender": "male", + "birthday": "01/02/03" }, "message": "Really a good post which showed me the better amplified iphone ", "created_time": "2010-10-03T04:58:05+0000" diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_feed b/webapp/plugins/facebook/tests/testdata/7568536355_feed index cb21e93633..b78e10b953 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_feed +++ b/webapp/plugins/facebook/tests/testdata/7568536355_feed @@ -49,7 +49,9 @@ "id": "10151728144886356_28166223", "from": { "name": "Paul Cassidy", - "id": "100000028811048" + "id": "100000028811048", + "gender": "male", + "birthday": "01/02/03" }, "message": "Maybe you should have added a 6th box labelled \"Safe from spying by your own government\". That one would go unticked for all of them, I'd say.", "can_remove": false, @@ -61,7 +63,9 @@ "id": "10151728144886356_28166189", "from": { "name": "Dave Myers", - "id": "44100633" + "id": "44100633", + "gender": "male", + "birthday": "01/02/03" }, "message": "Protect from everyone but the government, of course.", "can_remove": false, @@ -73,7 +77,9 @@ "id": "10151728144886356_28166962", "from": { "name": "Dave F. Samuel", - "id": "18417420" + "id": "18417420", + "gender": "male", + "birthday": "01/02/03" }, "message": "This is missing a 6th box... \"Hands data over to the NSA\"", "can_remove": false, @@ -85,7 +91,9 @@ "id": "10151728144886356_28166229", "from": { "name": "Alex Wright", - "id": "518042907" + "id": "518042907", + "gender": "male", + "birthday": "01/02/03" }, "message": "Well good thing the NSA doesn't give a crap and vacuums up all the data anyway.\n\nDon't think for a SECOND that large scale corporate and government forces don't use your data against you and have been since the early 1960's. \n\nEven implying that NSA doesn't already hold all of the crypto keys to unlock ALL of the encryption standards.\n\nWhat the HELL do you think they're building this for:\n\nhttp://en.wikipedia.org/wiki/Utah_Data_Center", "can_remove": false, @@ -97,7 +105,9 @@ "id": "10151728144886356_28166207", "from": { "name": "Ben Stanley", - "id": "1046979495" + "id": "1046979495", + "gender": "male", + "birthday": "01/02/03" }, "message": "Doesn't really matter if they keep 'improving' my privacy settings, though.", "can_remove": false, @@ -109,7 +119,9 @@ "id": "10151728144886356_28168085", "from": { "name": "Christina Rockwood", - "id": "1064132204" + "id": "1064132204", + "gender": "female", + "birthday": "01/02/03" }, "message": "\"Protecting\" my data? LOL!....interesting that i browse products on other websites only to find those exact same products appearing in the ads on my FB page later.... i wonder how FB could POSSIBLY have known the exact products i was browsing for on completely different websites.... coincidence? Doubtful... ", "can_remove": false, @@ -121,7 +133,9 @@ "id": "10151728144886356_28167048", "from": { "name": "Sean Lancaster", - "id": "22419216" + "id": "22419216", + "gender": "male", + "birthday": "01/02/03" }, "message": "\"everyone else, not so much\" as you proceed to show a chart showing Google and a few others with green nearly all across or all across. Are they not part of \"everyone else\"?", "can_remove": false, @@ -133,7 +147,9 @@ "id": "10151728144886356_28166298", "from": { "name": "Jan Gvdp", - "id": "61309586" + "id": "61309586", + "gender": "male", + "birthday": "01/02/03" }, "message": "of course you protect it, when you can sell it", "can_remove": false, @@ -145,7 +161,9 @@ "id": "10151728144886356_28168089", "from": { "name": "Leonardo Dutra", - "id": "100000047561578" + "id": "100000047561578", + "gender": "male", + "birthday": "01/02/03" }, "message": "Why is Facebook on the title if Google, and others, are one step ahead?", "can_remove": false, @@ -157,7 +175,9 @@ "id": "10151728144886356_28166548", "from": { "name": "Leo Gharapetian", - "id": "1280168112" + "id": "1280168112", + "gender": "male", + "birthday": "01/02/03" }, "message": "In progress and pending should not have a green box!", "can_remove": false, @@ -169,7 +189,9 @@ "id": "10151728144886356_28166372", "from": { "name": "Damon Freinik", - "id": "1655356726" + "id": "1655356726", + "gender": "male", + "birthday": "01/02/03" }, "message": "If you put it on the internets kiss it goodbye...", "can_remove": false, @@ -181,7 +203,9 @@ "id": "10151728144886356_28166201", "from": { "name": "Joe McLaughlin", - "id": "100001277313216" + "id": "100001277313216", + "gender": "male", + "birthday": "01/02/03" }, "message": "The difference is, Amazon dont catalog every bit of your being for evil. They just keep a log of what you bought so you can see it later..", "can_remove": false, @@ -193,7 +217,9 @@ "id": "10151728144886356_28175126", "from": { "name": "Joe Pepersack", - "id": "556987515" + "id": "556987515", + "gender": "male", + "birthday": "01/02/03" }, "message": "Of course they protect it. Your data is their product. If they let someone steal it, they can't sell it.", "can_remove": false, @@ -205,7 +231,9 @@ "id": "10151728144886356_28166457", "from": { "name": "Eric Tiberius Blue", - "id": "560992307" + "id": "560992307", + "gender": "male", + "birthday": "01/02/03" }, "message": "Of course they protect it. They want to make as much money off of it as possible.... and keep it safe for the NSA to review.", "can_remove": false, @@ -217,7 +245,9 @@ "id": "10151728144886356_28174387", "from": { "name": "Matt Stratton", - "id": "500557137" + "id": "500557137", + "gender": "male", + "birthday": "01/02/03" }, "message": "As mentioned, by some others, the whole \"I was looking at honda.com and then Facebook showed me ads for Hondas\" is not because Facebook is spying out you. \n\nIt's because Facebook and Honda participate in a data exchange where Honda sets a cookie that Facebook (or anyone else who is part of that exchange) can read/use. \n\nIs it morally right? Dunno. Is Facebook \"tracking you without your knowledge\"? Sorta. But not the way you think it is.", "can_remove": false, @@ -229,7 +259,9 @@ "id": "10151728144886356_28175518", "from": { "name": "Jarek Niskiewicz", - "id": "100000641653272" + "id": "100000641653272", + "gender": "male", + "birthday": "01/02/03" }, "message": "Sure they do, makes it so much more valuable to their clients :D", "can_remove": false, @@ -241,7 +273,9 @@ "id": "10151728144886356_28170157", "from": { "name": "Banlu Anumart", - "id": "100003784651128" + "id": "100003784651128", + "gender": "male", + "birthday": "01/02/03" }, "message": "Good guy Dropbox.", "can_remove": false, @@ -253,7 +287,9 @@ "id": "10151728144886356_28166302", "from": { "name": "Daniel Fugisawa", - "id": "564295957" + "id": "564295957", + "gender": "male", + "birthday": "01/02/03" }, "message": "Yeah, right. Just to sell it to the highest bidder for a higher price. And to give it away to all P5 + Israel intelligence services.", "can_remove": false, @@ -265,7 +301,9 @@ "id": "10151728144886356_28166245", "from": { "name": "Meghan Carnowski", - "id": "100000290596817" + "id": "100000290596817", + "gender": "female", + "birthday": "01/02/03" }, "message": "Richard?", "message_tags": [ @@ -286,7 +324,9 @@ "id": "10151728144886356_28176143", "from": { "name": "Chris Whitman", - "id": "100000891619230" + "id": "100000891619230", + "gender": "male", + "birthday": "01/02/03" }, "message": "It doesn't really matter how secure their databases are if hackers just get all of the data from all of them for hacking the nsa", "can_remove": false, @@ -298,7 +338,9 @@ "id": "10151728144886356_28173597", "from": { "name": "Michelle Genée", - "id": "888230692" + "id": "888230692", + "gender": "female", + "birthday": "01/02/03" }, "message": "Jonathan FYI", "message_tags": [ @@ -319,7 +361,9 @@ "id": "10151728144886356_28173177", "from": { "name": "Ma Sheila A Magboo", - "id": "1291663317" + "id": "1291663317", + "gender": "female", + "birthday": "01/02/03" }, "message": "Really?", "can_remove": false, @@ -331,7 +375,9 @@ "id": "10151728144886356_28171786", "from": { "name": "Diane Thomas", - "id": "1302225487" + "id": "1302225487", + "gender": "female", + "birthday": "01/02/03" }, "message": "Thank you, Brian Thomas, and Facebook crew!!! :)", "message_tags": [ @@ -352,7 +398,9 @@ "id": "10151728144886356_28171258", "from": { "name": "Marcus Randall", - "id": "1049255840" + "id": "1049255840", + "gender": "male", + "birthday": "01/02/03" }, "message": "Similar to comparing amored trucks vs pizza delivery drivers. Regardless, individuals are independant varriables when it comes to their privacy", "can_remove": false, @@ -416,7 +464,9 @@ "id": "7568536355_437894121355_13765492", "from": { "name": "Harald Chuchlik", - "id": "1479111505" + "id": "1479111505", + "gender": "male", + "birthday": "01/02/03" }, "message": "Troll somewhere else", "created_time": "2010-10-02T16:44:28+0000" @@ -425,7 +475,9 @@ "id": "7568536355_437894121355_13765969", "from": { "name": "Chad Settlemier", - "id": "535362181" + "id": "535362181", + "gender": "male", + "birthday": "01/02/03" }, "message": "What about iPhone 4 top ten Jailbroken apps?", "created_time": "2010-10-02T18:10:52+0000" diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_posts b/webapp/plugins/facebook/tests/testdata/7568536355_posts index 351664d2e5..e103bae340 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_posts +++ b/webapp/plugins/facebook/tests/testdata/7568536355_posts @@ -37,7 +37,9 @@ "id": "7568536355_437900891355_13767800", "from": { "name": "Adrian Valera", - "id": "833828624" + "id": "833828624", + "gender": "male", + "birthday": "01/02/03" }, "message": "Cool Top 10", "created_time": "2010-10-03T01:58:01+0000" @@ -46,7 +48,9 @@ "id": "7568536355_437900891355_13768553", "from": { "name": "Rahul Narasimha Akmol", - "id": "625158327" + "id": "625158327", + "gender": "male", + "birthday": "01/02/03" }, "message": "Really a good post which showed me the better amplified iphone ", "created_time": "2010-10-03T04:58:05+0000" @@ -90,7 +94,8 @@ "data": [ { "name": "Tigger Pike", - "id": "641265671" + "id": "641265671", + "gender": "male" } ], "count": 12 @@ -101,7 +106,9 @@ "id": "7568536355_437894121355_13765492", "from": { "name": "Harald Chuchlik", - "id": "1479111505" + "id": "1479111505", + "gender": "male", + "birthday": "01/02/03" }, "message": "Troll somewhere else", "created_time": "2010-10-02T16:44:28+0000" @@ -110,7 +117,9 @@ "id": "7568536355_437894121355_13765969", "from": { "name": "Chad Settlemier", - "id": "535362181" + "id": "535362181", + "gender": "male", + "birthday": "01/02/03" }, "message": "What about iPhone 4 top ten Jailbroken apps?", "created_time": "2010-10-02T18:10:52+0000" diff --git a/webapp/plugins/facebook/tests/testdata/7568536355_posts-limit=25-until=2010-09-29T19%3A49%3A57%2B0000-since=0 b/webapp/plugins/facebook/tests/testdata/7568536355_posts-limit=25-until=2010-09-29T19%3A49%3A57%2B0000-since=0 index 68c622df43..f194de0fd9 100644 --- a/webapp/plugins/facebook/tests/testdata/7568536355_posts-limit=25-until=2010-09-29T19%3A49%3A57%2B0000-since=0 +++ b/webapp/plugins/facebook/tests/testdata/7568536355_posts-limit=25-until=2010-09-29T19%3A49%3A57%2B0000-since=0 @@ -37,7 +37,9 @@ "id": "7568536355_437660146355_13761242", "from": { "name": "Christene Snively", - "id": "100001078428730" + "id": "100001078428730", + "gender": "female", + "birthday": "01/02/03" }, "message": "Five things every girl does before she meets her boyfriend - LOL!\n\nhttp://www.facebook.com/profile.php?id=164464470231080", "created_time": "2010-10-01T19:54:26+0000" @@ -46,7 +48,9 @@ "id": "7568536355_437660146355_13761476", "from": { "name": "Bill Goodman", - "id": "1184584231" + "id": "1184584231", + "gender": "male", + "birthday": "01/02/03" }, "message": "^^^^^^^^^^^^^^^^\nspammer", "created_time": "2010-10-01T20:35:35+0000" @@ -55,7 +59,9 @@ "id": "7568536355_437660146355_13763960", "from": { "name": "Kenneth Lam", - "id": "533302090" + "id": "533302090", + "gender": "male", + "birthday": "01/02/03" }, "message": "I use SUPER (freeware)\nhttp://www.erightsoft.com/SUPER.html", "created_time": "2010-10-02T08:19:38+0000" diff --git a/webapp/plugins/facebook/tests/testdata/833828624 b/webapp/plugins/facebook/tests/testdata/833828624 index 13759b4bbd..eea4f2d4a1 100644 --- a/webapp/plugins/facebook/tests/testdata/833828624 +++ b/webapp/plugins/facebook/tests/testdata/833828624 @@ -4,6 +4,8 @@ "first_name": "Adrian", "last_name": "Valera", "link": "http://www.facebook.com/avo77", + "gender": "male", + "birthday": "01/02/03", "locale": "es_LA", "updated_time": "2010-06-13T00:37:21+0000" } \ No newline at end of file diff --git a/webapp/plugins/facebook/tests/testdata/me b/webapp/plugins/facebook/tests/testdata/me index 18cd4fa823..7fedb5b301 100644 --- a/webapp/plugins/facebook/tests/testdata/me +++ b/webapp/plugins/facebook/tests/testdata/me @@ -3,7 +3,9 @@ "name": "Gina Trapani", "location": { "id": "110714572282163", - "name": "San Diego, California" + "name": "San Diego, California", + "gender": "female", + "birthday": "01/02/03" }, "about": "Blogger and software developer. Project Director at Expert Labs. Co-host of This Week in Google." } \ No newline at end of file diff --git a/webapp/plugins/insightsgenerator/insights/ageanalysis.php b/webapp/plugins/insightsgenerator/insights/ageanalysis.php new file mode 100644 index 0000000000..6ffc74a90a --- /dev/null +++ b/webapp/plugins/insightsgenerator/insights/ageanalysis.php @@ -0,0 +1,150 @@ +. + * + * + * AgeAnalysis (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ +class AgeAnalysisInsight extends InsightPluginParent implements InsightPlugin { + public function generateInsight(Instance $instance, $last_week_of_posts, $number_days) { + if ($instance->network == 'facebook') { + parent::generateInsight ( $instance, $last_week_of_posts, $number_days ); + $this->logger->logInfo ( "Begin generating insight", __METHOD__ . ',' . __LINE__ ); + + $insight_baseline_dao = DAOFactory::getDAO ( 'InsightBaselineDAO' ); + $filename = basename ( __FILE__, ".php" ); + + if (self::shouldGenerateInsight ( 'age_analysis', $instance, $regenerate_existing_insight = true )) { + $post_dao = DAOFactory::getDAO ( 'PostDAO' ); + $fpost_dao = DAOFactory::getDAO ( 'FavoritePostDAO' ); + $posts = $post_dao->getMostFavCommentPostsByUserId ( $instance->network_user_id, $instance->network ); + foreach ( $posts as $post ) { + $birthdays_fav = $fpost_dao->getBirthdayOfFavoriters ( $post->post_id ); + $birthdays_comm = $fpost_dao->getBirthdayOfCommenters ( $post->post_id ); + $age_data = array ( + '18' => 0, + '18_25' => 0, + '25_35' => 0, + '35_45' => 0, + '45' => 0 + ); + + foreach ( $birthdays_comm as $birthday_comm ) { + $birthday = strtotime ( $birthday_comm ); + if ($birthday === false) { + return false; + } + + $age = date ( 'Y' ) - date ( 'Y', $birthday ); + if (date ( 'md' ) < date ( 'md', $birthday )) { + $age --; + } + + if ($age > 0 & $age < 18) { + $age_data ['18'] ++; + } elseif ($age >= 18 & $age < 25) { + $age_data ['18_25'] ++; + } elseif ($age >= 25 & $age < 35) { + $age_data ['25_35'] ++; + } elseif ($age >= 35 & $age < 45) { + $age_data ['35_45'] ++; + } elseif ($age >= 45) { + $age_data ['45'] ++; + } + } + + foreach ( $birthdays_fav as $birthday_fav ) { + $birthday = strtotime ( $birthday_fav ); + if ($birthday === false) { + return false; + } + $age = date ( 'Y' ) - date ( 'Y', $birthday ); + if (date ( 'md' ) < date ( 'md', $birthday )) { + $age --; + } + + if ($age > 0 & $age < 18) { + $age_data ['18'] ++; + } elseif ($age >= 18 & $age < 25) { + $age_data ['18_25'] ++; + } elseif ($age >= 25 & $age < 35) { + $age_data ['25_35'] ++; + } elseif ($age >= 35 & $age < 45) { + $age_data ['35_45'] ++; + } elseif ($age >= 45) { + $age_data ['45'] ++; + } + } + + $simplified_post_date = date ( 'Y-m-d', strtotime ( $post->pub_date ) ); + + if (max ( $age_data ) == $age_data ['18']) { + $this->insight_dao->insertInsightDeprecated ( 'age_analysis', $instance->id, + $simplified_post_date, "Teens!", "" . number_format ( $age_data ['18'] ) . + " times people at the age less than 18 years interested in " . + $instance->network_username . "'s post", $filename, Insight::EMPHASIS_HIGH, + serialize ( array ($post, $age_data ) ) ); + } elseif (max ( $age_data ) == $age_data ['18_25']) { + $this->insight_dao->insertInsightDeprecated ( 'age_analysis', $instance->id, + $simplified_post_date, "So young!", "" . number_format ( $age_data ['18_25'] ) . + " times people aged 18 to 25 years interested in " . + $instance->network_username . "'s post", $filename, Insight::EMPHASIS_HIGH, + serialize ( array ($post, $age_data ) ) ); + } elseif (max ( $age_data ) == $age_data ['25_35']) { + $this->insight_dao->insertInsightDeprecated ( 'age_analysis', $instance->id, + $simplified_post_date, "Oh, adults!", "" . number_format ( $age_data ['25_35'] ) + . " times people aged 25 to 35 years interested in " . + $instance->network_username . "'s post", $filename, Insight::EMPHASIS_HIGH, + serialize ( array ($post, $age_data ) ) ); + } elseif (max ( $age_data ) == $age_data ['35_45']) { + $this->insight_dao->insertInsightDeprecated ( 'age_analysis', $instance->id, + $simplified_post_date, "Middle-aged!", "" .number_format ( $age_data ['35_45'] ) + . " times peopleaged 35 to 45 years interested in " . + $instance->network_username . "'s post", $filename, Insight::EMPHASIS_HIGH, + serialize ( array ($post, $age_data ) ) ); + } elseif (max ( $age_data ) == $age_data ['45']) { + $this->insight_dao->insertInsightDeprecated ( 'age_analysis', $instance->id, + $simplified_post_date, "Seniors!", "" . number_format ( $age_data ['45'] ) . + " times people at the age more than 45 years interested in " . + $instance->network_username . "'s post", $filename, Insight::EMPHASIS_HIGH, + serialize ( array ($post, $age_data ) ) ); + } + } + $this->logger->logInfo ( "Done generating insight", __METHOD__ . ',' . __LINE__ ); + } + } + } +} +$insights_plugin_registrar = PluginRegistrarInsights::getInstance (); +$insights_plugin_registrar->registerInsightPlugin ( 'AgeAnalysisInsight' ); + diff --git a/webapp/plugins/insightsgenerator/insights/biggestfans.php b/webapp/plugins/insightsgenerator/insights/biggestfans.php index 394472b586..a8f1c9132f 100644 --- a/webapp/plugins/insightsgenerator/insights/biggestfans.php +++ b/webapp/plugins/insightsgenerator/insights/biggestfans.php @@ -1,4 +1,4 @@ -getUsersWhoFavoritedMostOfYourPosts($instance->network_user_id, $instance->network, 7); if (isset($fans) && sizeof($fans) > 0 ) { diff --git a/webapp/plugins/insightsgenerator/insights/genderanalysis.php b/webapp/plugins/insightsgenerator/insights/genderanalysis.php new file mode 100644 index 0000000000..341df0fa7a --- /dev/null +++ b/webapp/plugins/insightsgenerator/insights/genderanalysis.php @@ -0,0 +1,92 @@ +. + * + * + * GenderAnalysis (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ +class GenderAnalysisInsight extends InsightPluginParent implements InsightPlugin { + public function generateInsight(Instance $instance, $last_week_of_posts, $number_days) { + if ($instance->network == 'facebook') { + parent::generateInsight ( $instance, $last_week_of_posts, $number_days ); + $this->logger->logInfo ( "Begin generating insight", __METHOD__ . ',' . __LINE__ ); + + $insight_baseline_dao = DAOFactory::getDAO ( 'InsightBaselineDAO' ); + $filename = basename ( __FILE__, ".php" ); + + if (self::shouldGenerateInsight ( 'gender_analysis', $instance )) { + $post_dao = DAOFactory::getDAO ( 'PostDAO' ); + $fpost_dao = DAOFactory::getDAO ( 'FavoritePostDAO' ); + $posts = $post_dao->getMostFavCommentPostsByUserId ( $instance->network_user_id, $instance->network ); + foreach ( $posts as $post ) { + $gender_fav = $fpost_dao->getGenderOfFavoriters ( $post->post_id ); + $gender_comm = $fpost_dao->getGenderOfCommenters ( $post->post_id ); + + $female = $gender_fav ['female_likes_count'] + $gender_comm ['female_comm_count']; + $male = $gender_fav ['male_likes_count'] + $gender_comm ['male_comm_count']; + + $gender_data = array ( + 'gender' => 'value', + 'female' => $female, + 'male' => $male + ); + $simplified_post_date = date ( 'Y-m-d', strtotime ( $post->pub_date ) ); + echo "time= " . $simplified_post_date; + + if ($female > $male) { + $this->insight_dao->insertInsightDeprecated ( 'gender_analysis', $instance->id, + $simplified_post_date, "Women favorite!", "" . number_format ( $female ) . + " times women interested in " . $instance->network_username . "'s post", + $filename, Insight::EMPHASIS_HIGH, serialize ( array ($post, $gender_data) ) ); + } elseif ($male > $female) { + $this->insight_dao->insertInsightDeprecated ( 'gender_analysis', $instance->id, + $simplified_post_date, "Men favorite!", "" . number_format ( $male ) . + " times men interested in " . $instance->network_username . "'s post", + $filename, Insight::EMPHASIS_HIGH, serialize ( array ($post, $gender_data) ) ); + } else { + $this->insight_dao->insertInsightDeprecated ( 'gender_analysis', $instance->id, + $simplified_post_date, "Loved by all!", "" . number_format ( $female + $male ) . + " times women and men interested in " . $instance->network_username . + "'s post", $filename, Insight::EMPHASIS_HIGH, serialize ( array ($post, $gender_data) ) ); + } + } + $this->logger->logInfo ( "Done generating insight", __METHOD__ . ',' . __LINE__ ); + } + } + } +} +$insights_plugin_registrar = PluginRegistrarInsights::getInstance (); +$insights_plugin_registrar->registerInsightPlugin ( 'GenderAnalysisInsight' ); + + + diff --git a/webapp/plugins/insightsgenerator/insights/geoanalysisfacebook.php b/webapp/plugins/insightsgenerator/insights/geoanalysisfacebook.php new file mode 100644 index 0000000000..3baab5beea --- /dev/null +++ b/webapp/plugins/insightsgenerator/insights/geoanalysisfacebook.php @@ -0,0 +1,93 @@ +. + * + * + * GeoAnalysisFacebook (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ +class GeoAnalysisFacebookInsight extends InsightPluginParent implements InsightPlugin { + public function generateInsight(Instance $instance, $last_week_of_posts, $number_days) { + if ($instance->network == 'facebook') { + parent::generateInsight ( $instance, $last_week_of_posts, $number_days ); + $this->logger->logInfo ( "Begin generating insight", __METHOD__ . ',' . __LINE__ ); + + $insight_baseline_dao = DAOFactory::getDAO ( 'InsightBaselineDAO' ); + $filename = basename ( __FILE__, ".php" ); + + if (self::shouldGenerateInsight ( 'geo_analysis_facebook', $instance, $insight_date = 'today', + $regenerate_existing_insight = true, $day_of_week = 5, count ( $last_week_of_posts ) )) { + $fpost_dao = DAOFactory::getDAO ( 'FavoritePostDAO' ); + $geo_data = array (); + foreach ( $last_week_of_posts as $post ) { + $locations_fav = $fpost_dao->getLocationOfFavoriters ( $post->post_id ); + $locations_comm = $fpost_dao->getLocationOfCommenters ( $post->post_id ); + $geos = array_merge ( $locations_comm, $locations_fav ); + // extracting name of city from location + foreach ( $geos as $geo ) { + $pos = strpos ( $geo ['location'], "," ); + if ($pos == 0) { + $city = $geo ['location']; + } else { + $city = substr ( $geo ['location'], 0, $pos ); + } + array_push ( $geo_data, array ( + "name" => $geo ['name'], + "city" => $city + ) ); + } + } + // getting unique users + $geo_data = array_map ( "unserialize", array_unique ( array_map ( "serialize", $geo_data ) ) ); + $count = count ( $geo_data ); + // grouping of users with the same locations + for($i = 0; $i <= count ( $geo_data ) - 1; $i ++) { + for($j = $i + 1; $j <= count ( $geo_data ) - 1; $j ++) { + if ($geo_data [$i] ['city'] == $geo_data [$j] ['city']) { + $geo_data [$i] ['name'] = $geo_data [$i] ['name'] . ", " . $geo_data [$j] ['name']; + unset ( $geo_data [$j] ); + } + } + } + $this->insight_dao->insertInsightDeprecated ( 'geo_analysis_facebook', $instance->id, + $this->insight_date, "All over the world", "" . number_format ( $count ) . + " people interested in " . $instance->network_username . "'s posts last week", + $filename, Insight::EMPHASIS_HIGH, serialize ( array ($geo_data) ) ); + + $this->logger->logInfo ( "Done generating insight", __METHOD__ . ',' . __LINE__ ); + } + } + } +} +$insights_plugin_registrar = PluginRegistrarInsights::getInstance (); +$insights_plugin_registrar->registerInsightPlugin ( 'GeoAnalysisFacebookInsight' ); + diff --git a/webapp/plugins/insightsgenerator/insights/geoanalysistwitter.php b/webapp/plugins/insightsgenerator/insights/geoanalysistwitter.php new file mode 100644 index 0000000000..b911a72d6f --- /dev/null +++ b/webapp/plugins/insightsgenerator/insights/geoanalysistwitter.php @@ -0,0 +1,82 @@ +. + * + * + * GeoAnalysisTwitter (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ +class GeoAnalysisTwitterInsight extends InsightPluginParent implements InsightPlugin { + public function generateInsight(Instance $instance, $last_week_of_posts, $number_days) { + if ($instance->network == 'twitter') { + parent::generateInsight ( $instance, $last_week_of_posts, $number_days ); + $this->logger->logInfo ( "Begin generating insight", __METHOD__ . ',' . __LINE__ ); + + $filename = basename ( __FILE__, ".php" ); + + if (self::shouldGenerateInsight ( 'geo_analysis_twitter', $instance, $insight_date = 'today', + $regenerate_existing_insight = true, $day_of_week = 4, count ( $last_week_of_posts ) )) { + $fpost_dao = DAOFactory::getDAO ( 'FavoritePostDAO' ); + $geo_data = array (); + $geos = $fpost_dao->getGeoOfPostsFromOneWeekAgo ( $instance->network_user_id ); + + foreach ( $geos as $geo ) { + $geo_str = trim ( $geo ['geo'] ); + $pos = strpos ( $geo_str, "," ); + $lat = substr ( $geo_str, 0, $pos - 3 ); + $long = substr ( $geo_str, $pos + 1, strlen ( $geo_str ) - $pos - 4 ); + array_push ( $geo_data, array ( + "lat" => $lat, + "long" => $long, + "place" => $geo ['place'] + ) + ); + } + + $geo_data = array_map ( "unserialize", array_unique ( array_map ( "serialize", $geo_data ) ) ); + + $this->insight_dao->insertInsightDeprecated ( 'geo_analysis_twitter', $instance->id, + $this->insight_date, "Here am I!", "" . number_format ( count ( $geo_data ) ) . + " places were used by " . $this->username . " to make tweets last week", + $filename, Insight::EMPHASIS_HIGH, serialize ( array ($geo_data ) ) ); + + $this->logger->logInfo ( "Done generating insight", __METHOD__ . ',' . __LINE__ ); + } + } + } +} + +$insights_plugin_registrar = PluginRegistrarInsights::getInstance (); +$insights_plugin_registrar->registerInsightPlugin ( 'GeoAnalysisTwitterInsight' ); + diff --git a/webapp/plugins/insightsgenerator/insights/topofpostsfacebook.php b/webapp/plugins/insightsgenerator/insights/topofpostsfacebook.php new file mode 100644 index 0000000000..fe03475a7f --- /dev/null +++ b/webapp/plugins/insightsgenerator/insights/topofpostsfacebook.php @@ -0,0 +1,87 @@ +. + * + * + * TopOfPostsFacebook (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ + +class TopOfPostsFacebookInsight extends InsightPluginParent implements InsightPlugin { + + public function generateInsight(Instance $instance, $last_week_of_posts, $number_days) { + parent::generateInsight($instance, $last_week_of_posts, $number_days); + $this->logger->logInfo("Begin generating insight", __METHOD__.','.__LINE__); + + $since_date = date("Y-m-d"); + + $insight_date = new DateTime(); + $insight_day_of_week = (int) $insight_date->format('w'); + $insight_day_of_month = (int) $insight_date->format('j'); + + $filename = basename(__FILE__, ".php"); + + if ($insight_day_of_month == 1) { //it's the first day of the month + // Past 30 days + if (self::shouldGenerateInsight('top_of_posts_30_days', $instance, $insight_date=$since_date, + $regenerate_existing_insight=true)) { + $post_dao = DAOFactory::getDAO('PostDAO'); + $posts = $post_dao->getMostSharedPostsOfTheLastDays($instance->network_user_id, + $instance->network, 30); + if (isset($posts) && sizeof($posts) > 0 ) { + $this->insight_dao->insertInsightDeprecated("top_of_posts_30_days", $instance->id, + $since_date, "Top posts:", "Most shared $this->username's " + .$this->terms->getNoun('post', InsightTerms::PLURAL)." of the last 30 days: ", + $filename, Insight::EMPHASIS_LOW, serialize($posts)); + } + } + } else if ($insight_day_of_week == 1) { //it's Sunday + // Past 7 days + if (self::shouldGenerateInsight('top_of_posts_7_days', $instance, $insight_date=$since_date, + $regenerate_existing_insight=true)) { + $post_dao = DAOFactory::getDAO('PostDAO'); + $posts = $post_dao->getMostSharedPostsOfTheLastDays($instance->network_user_id, + $instance->network, 7); + if (isset($posts) && sizeof($posts) > 0 ) { + $this->insight_dao->insertInsightDeprecated("top_of_posts_7_days", $instance->id, + $since_date, "Top posts:", "Most shared $this->username's " + .$this->terms->getNoun('post', InsightTerms::PLURAL)." of the last 7 days: ", + $filename, Insight::EMPHASIS_LOW, serialize($posts)); + } + } + } + $this->logger->logInfo("Done generating insight", __METHOD__.','.__LINE__); + } +} + +$insights_plugin_registrar = PluginRegistrarInsights::getInstance(); +$insights_plugin_registrar->registerInsightPlugin('TopOfPostsFacebookInsight'); + diff --git a/webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php b/webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php index 1d82b3f95c..a418f77a92 100644 --- a/webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php +++ b/webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php @@ -98,7 +98,7 @@ public function shouldGenerateInsight($slug, Instance $instance, $insight_date=n $existing_insight = $this->insight_dao->getInsight($slug, $instance->id, date('Y-m-d', strtotime($insight_date))); - + if (isset($existing_insight)) { $run = $run && false; } else { diff --git a/webapp/plugins/insightsgenerator/tests/TestOfAgeAnalysisInsight.php b/webapp/plugins/insightsgenerator/tests/TestOfAgeAnalysisInsight.php new file mode 100644 index 0000000000..37d47ed6fc --- /dev/null +++ b/webapp/plugins/insightsgenerator/tests/TestOfAgeAnalysisInsight.php @@ -0,0 +1,442 @@ +. + * + * + * AgeAnalysis (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ + +require_once dirname(__FILE__) . '/../../../../tests/init.tests.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/autorun.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/web_tester.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/insights/ageanalysis.php'; + +class TestOfAgeAnalysisInsight extends ThinkUpUnitTestCase { + + public function setUp() { + parent::setUp(); + } + + public function tearDown() { + parent::tearDown(); + } + + public function testAgeAnalysisForFacebookTeens() { + // Get data ready that insight requires + $builders = self::buildDataTeens(); + + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 8654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new AgeAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('age_analysis', 100, $today); + $age_data = unserialize($result->related_data); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Teens!'); + $this->assertIsA($age_data, "array"); + $this->assertIsA($age_data[0], "Post"); + $this->assertEqual(count($age_data), 2); + $this->assertEqual($age_data[1]['18'], 2); + $this->assertEqual($age_data[1]['18_25'], 0); + $this->assertEqual($age_data[1]['25_35'], 0); + $this->assertEqual($age_data[1]['35_45'], 0); + $this->assertEqual($age_data[1]['45'], 0); + } + + public function testAgeAnalysisForFacebookYoung() { + // Get data ready that insight requires + $builders = self::buildDataYoung(); + + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 8654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new AgeAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('age_analysis', 100, $today); + $age_data = unserialize($result->related_data); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'So young!'); + $this->assertIsA($age_data, "array"); + $this->assertIsA($age_data[0], "Post"); + $this->assertEqual(count($age_data), 2); + $this->assertEqual($age_data[1]['18'], 1); + $this->assertEqual($age_data[1]['18_25'], 2); + $this->assertEqual($age_data[1]['25_35'], 0); + $this->assertEqual($age_data[1]['35_45'], 0); + $this->assertEqual($age_data[1]['45'], 0); + } + + public function testAgeAnalysisForFacebookAdults() { + // Get data ready that insight requires + $builders = self::buildDataAdults(); + + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 8654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new AgeAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('age_analysis', 100, $today); + $age_data = unserialize($result->related_data); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Oh, adults!'); + $this->assertIsA($age_data, "array"); + $this->assertIsA($age_data[0], "Post"); + $this->assertEqual(count($age_data), 2); + $this->assertEqual($age_data[1]['18'], 1); + $this->assertEqual($age_data[1]['18_25'], 0); + $this->assertEqual($age_data[1]['25_35'], 2); + $this->assertEqual($age_data[1]['35_45'], 0); + $this->assertEqual($age_data[1]['45'], 0); + } + + public function testAgeAnalysisForFacebookMids() { + // Get data ready that insight requires + $builders = self::buildDataMids(); + + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 8654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new AgeAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('age_analysis', 100, $today); + $age_data = unserialize($result->related_data); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Middle-aged!'); + $this->assertIsA($age_data, "array"); + $this->assertIsA($age_data[0], "Post"); + $this->assertEqual(count($age_data), 2); + $this->assertEqual($age_data[1]['18'], 1); + $this->assertEqual($age_data[1]['18_25'], 0); + $this->assertEqual($age_data[1]['25_35'], 0); + $this->assertEqual($age_data[1]['35_45'], 2); + $this->assertEqual($age_data[1]['45'], 0); + } + + public function testAgeAnalysisForFacebookSeniors() { + // Get data ready that insight requires + $builders = self::buildDataSeniors(); + + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 8654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new AgeAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('age_analysis', 100, $today); + $age_data = unserialize($result->related_data); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Seniors!'); + $this->assertIsA($age_data, "array"); + $this->assertIsA($age_data[0], "Post"); + $this->assertEqual(count($age_data), 2); + $this->assertEqual($age_data[1]['18'], 1); + $this->assertEqual($age_data[1]['18_25'], 0); + $this->assertEqual($age_data[1]['25_35'], 0); + $this->assertEqual($age_data[1]['35_45'], 0); + $this->assertEqual($age_data[1]['45'], 2); + } + + private function buildDataTeens() { + $builders_male = array(); + + $now = date('Y-m-d H:i:s'); + + $builders[] = FixtureBuilder::build('posts', array('id'=>233, 'post_id'=>233, 'author_user_id'=>8654321, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>2, 'is_protected'=>0,'favlike_count_cache' => 2)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>234, 'post_id'=>234, 'author_user_id'=>8654320, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>237, 'post_id'=>237, 'author_user_id'=>8654324, + 'author_username'=>'user4', 'author_fullname'=>'User4', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 3.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654320, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654324, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654320, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'birthday'=> '06/23/2002', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654321, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'birthday'=> '08/01/1994', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654322, 'user_name'=>'user2', + 'full_name'=>'User2', 'gender'=>'female', 'birthday'=> '06/23/1985', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654323, 'user_name'=>'user3', + 'full_name'=>'User3', 'gender'=>'male', 'birthday'=> '06/23/1975', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654324, 'user_name'=>'user4', + 'full_name'=>'User4', 'gender'=>'female', 'birthday'=> '06/23', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders; + } + + private function buildDataYoung() { + $builders_male = array(); + + $now = date('Y-m-d H:i:s'); + + $builders[] = FixtureBuilder::build('posts', array('id'=>233, 'post_id'=>233, 'author_user_id'=>8654321, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>4, 'is_protected'=>0,'favlike_count_cache' => 4)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>234, 'post_id'=>234, 'author_user_id'=>8654320, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>235, 'post_id'=>235, 'author_user_id'=>8654321, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654321, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654320, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'birthday'=> '06/23/2010', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654321, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'birthday'=> '08/01/1994', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654322, 'user_name'=>'user2', + 'full_name'=>'User2', 'gender'=>'female', 'birthday'=> '06/23/1985', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654323, 'user_name'=>'user3', + 'full_name'=>'User3', 'gender'=>'male', 'birthday'=> '06/23/1975', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654324, 'user_name'=>'user4', + 'full_name'=>'User4', 'gender'=>'female', 'birthday'=> '06/23', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders; + } + + private function buildDataAdults() { + $builders_male = array(); + + $now = date('Y-m-d H:i:s'); + + $builders[] = FixtureBuilder::build('posts', array('id'=>233, 'post_id'=>233, 'author_user_id'=>8654321, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>4, 'is_protected'=>0,'favlike_count_cache' => 4)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>234, 'post_id'=>234, 'author_user_id'=>8654320, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>235, 'post_id'=>235, 'author_user_id'=>8654322, + 'author_username'=>'user2', 'author_fullname'=>'User2', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654322, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654320, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'birthday'=> '06/23/2010', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654321, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'birthday'=> '08/01/1994', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654322, 'user_name'=>'user2', + 'full_name'=>'User2', 'gender'=>'female', 'birthday'=> '06/23/1985', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654323, 'user_name'=>'user3', + 'full_name'=>'User3', 'gender'=>'male', 'birthday'=> '06/23/1975', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654324, 'user_name'=>'user4', + 'full_name'=>'User4', 'gender'=>'female', 'birthday'=> '06/23', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders; + } + + private function buildDataMids() { + $builders_male = array(); + + $now = date('Y-m-d H:i:s'); + + $builders[] = FixtureBuilder::build('posts', array('id'=>233, 'post_id'=>233, 'author_user_id'=>8654321, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>4, 'is_protected'=>0,'favlike_count_cache' => 4)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>234, 'post_id'=>234, 'author_user_id'=>8654320, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>235, 'post_id'=>235, 'author_user_id'=>8654323, + 'author_username'=>'user3', 'author_fullname'=>'User3', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654323, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654320, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'birthday'=> '06/23/2010', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654321, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'birthday'=> '08/01/1994', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654322, 'user_name'=>'user2', + 'full_name'=>'User2', 'gender'=>'female', 'birthday'=> '06/23/1985', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654323, 'user_name'=>'user3', + 'full_name'=>'User3', 'gender'=>'male', 'birthday'=> '06/23/1975', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654324, 'user_name'=>'user4', + 'full_name'=>'User4', 'gender'=>'female', 'birthday'=> '06/23', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders; + } + + private function buildDataSeniors() { + $builders_male = array(); + + $now = date('Y-m-d H:i:s'); + + $builders[] = FixtureBuilder::build('posts', array('id'=>233, 'post_id'=>233, 'author_user_id'=>8654321, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>4, 'is_protected'=>0,'favlike_count_cache' => 4)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>234, 'post_id'=>234, 'author_user_id'=>8654320, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>235, 'post_id'=>235, 'author_user_id'=>8654324, + 'author_username'=>'user4', 'author_fullname'=>'User4', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654324, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654320, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'birthday'=> '06/23/2010', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654321, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'birthday'=> '08/01/1994', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654322, 'user_name'=>'user2', + 'full_name'=>'User2', 'gender'=>'female', 'birthday'=> '06/23/1985', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654323, 'user_name'=>'user3', + 'full_name'=>'User3', 'gender'=>'male', 'birthday'=> '06/23/1975', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>8654324, 'user_name'=>'user4', + 'full_name'=>'User4', 'gender'=>'female', 'birthday'=> '06/23/1965', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders; + } +} + diff --git a/webapp/plugins/insightsgenerator/tests/TestOfAllAboutYouInsight.php b/webapp/plugins/insightsgenerator/tests/TestOfAllAboutYouInsight.php index 3c9a70ac2b..d234f4a56c 100644 --- a/webapp/plugins/insightsgenerator/tests/TestOfAllAboutYouInsight.php +++ b/webapp/plugins/insightsgenerator/tests/TestOfAllAboutYouInsight.php @@ -54,7 +54,7 @@ public function testCountFirstPersonReferences() { $this->assertEqual($count, 2); $count = AllAboutYouInsight::countFirstPersonReferences( - "New YearÕs Eve! Feeling very gay today, but not very homosexual."); + "New Year�s Eve! Feeling very gay today, but not very homosexual."); $this->assertEqual($count, 0); $count = AllAboutYouInsight::countFirstPersonReferences("Tis the season for adorable cards w/ photos of my ". @@ -85,6 +85,7 @@ public function testAllAboutYouInsightNoPriorBaseline() { $today = date ('Y-m-d'); $result = $insight_dao->getInsight('all_about_you', 10, $today); $this->debug(Utils::varDumpToString($result)); + $this->assertNotNull($result); $this->assertIsA($result, "Insight"); $this->assertPattern('/\@testeriffic\'s tweets contained the words/', $result->text); @@ -237,7 +238,7 @@ private function getTestPostObjects() { $post_text_arr[] = "I don't know, really? I thought so."; $post_text_arr[] = "Now that I'm back on Android, realizing just how under sung Google Now is. ". "I want it everywhere."; - $post_text_arr[] = "New YearÕs Eve! Feeling very gay today, but not very homosexual."; + $post_text_arr[] = "New Year�s Eve! Feeling very gay today, but not very homosexual."; $post_text_arr[] = "Took 1 firearms safety class to realize my ". "fantasy of stopping an attacker was just that: http://bit.ly/mybH2j Slate: http://slate.me/T6vwde"; $post_text_arr[] = "When @anildash told me he was writing this I was ". diff --git a/webapp/plugins/insightsgenerator/tests/TestOfGenderAnalysisInsight.php b/webapp/plugins/insightsgenerator/tests/TestOfGenderAnalysisInsight.php new file mode 100644 index 0000000000..e38fd83507 --- /dev/null +++ b/webapp/plugins/insightsgenerator/tests/TestOfGenderAnalysisInsight.php @@ -0,0 +1,267 @@ +. + * + * + * GenderAnalysis (name of file) + * + * Description of what this class does + * + * Copyright (c) 2013 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 Anna Shkerina + */ + +require_once dirname(__FILE__) . '/../../../../tests/init.tests.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/autorun.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/web_tester.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/insights/genderanalysis.php'; + +class TestOfGenderAnalysisInsight extends ThinkUpUnitTestCase { + + public function setUp() { + parent::setUp(); + } + + public function tearDown() { + parent::tearDown(); + } + + + public function testGenderAnalysisForFaceBookWomenFavotire() { + // Get data ready that insight requires + $builders = self::buildDataForFemale(); + + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 9654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new GenderAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('gender_analysis', 100, $today); + $gender_data = unserialize($result->related_data); + $this->debug(Utils::varDumpToString($result)); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Women favorite!'); + $this->assertIsA($gender_data, "array"); + $this->assertIsA($gender_data[0], "Post"); + $this->assertEqual(count($gender_data), 2); + $this->assertEqual($gender_data[1]['female'], 3); + $this->assertEqual($gender_data[1]['male'], 2); + } + + public function testGenderAnalysisForFaceBookMenFavotire() { + // Get data ready that insight requires + $builders = self::buildDataForMale(); + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 8654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new GenderAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('gender_analysis', 100, $today); + $gender_data = unserialize($result->related_data); + $this->debug(Utils::varDumpToString($result)); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Men favorite!'); + $this->assertIsA($gender_data, "array"); + $this->assertIsA($gender_data[0], "Post"); + $this->assertEqual(count($gender_data), 2); + $this->assertEqual($gender_data[1]['female'], 2); + $this->assertEqual($gender_data[1]['male'], 3); + } + + public function testGenderAnalysisForFaceBookAllFavotire() { + // Get data ready that insight requires + $builders = self::buildDataForAll(); + $instance = new Instance(); + $instance->id = 100; + $instance->network_user_id = 5654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + $insight_plugin = new GenderAnalysisInsight(); + $insight_plugin->generateInsight($instance, $last_week_of_posts, 1); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('gender_analysis', 100, $today); + $gender_data = unserialize($result->related_data); + $this->debug(Utils::varDumpToString($result)); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Loved by all!'); + $this->assertIsA($gender_data, "array"); + $this->assertIsA($gender_data[0], "Post"); + $this->assertEqual(count($gender_data), 2); + $this->assertEqual($gender_data[1]['female'], 2); + $this->assertEqual($gender_data[1]['male'], 2); + } + + private function buildDataForFemale() { + $builders_female = array(); + + $now = date('Y-m-d H:i:s'); + $yesterday = date('Y-m-d H:i:s', strtotime('yesterday')); + + $builders_female[] = FixtureBuilder::build('posts', array('id'=>333, 'post_id'=>333, 'author_user_id'=>9654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>3, 'is_protected'=>0,'favlike_count_cache' => 2)); + + $builders_female[] = FixtureBuilder::build('posts', array('id'=>334, 'post_id'=>334, 'author_user_id'=>9654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 333)); + + $builders_female[] = FixtureBuilder::build('posts', array('id'=>335, 'post_id'=>335, 'author_user_id'=>9654320, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 333)); + + $builders_female[] = FixtureBuilder::build('posts', array('id'=>336, 'post_id'=>336, 'author_user_id'=>9654320, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 2.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 333)); + + $builders_female[] = FixtureBuilder::build('favorites', array('post_id'=>333, 'author_user_id'=>9654321, + 'fav_of_user_id'=>9654321, 'network'=>'facebook')); + + $builders_female[] = FixtureBuilder::build('favorites', array('post_id'=>333, 'author_user_id'=>9654321, + 'fav_of_user_id'=>9654320, 'network'=>'facebook')); + + $builders_female[] = FixtureBuilder::build('users', array('user_id'=>9654321, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'male', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders_female[] = FixtureBuilder::build('users', array('user_id'=>9654320, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'female', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders_female; + + } + + private function buildDataForMale() { + $builders_male = array(); + + $now = date('Y-m-d H:i:s'); + $yesterday = date('Y-m-d H:i:s', strtotime('yesterday')); + + $builders_male[] = FixtureBuilder::build('posts', array('id'=>233, 'post_id'=>233, 'author_user_id'=>8654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>3, 'is_protected'=>0,'favlike_count_cache' => 2)); + + $builders_male[] = FixtureBuilder::build('posts', array('id'=>234, 'post_id'=>234, 'author_user_id'=>8654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 233)); + + $builders_male[] = FixtureBuilder::build('posts', array('id'=>235, 'post_id'=>235, 'author_user_id'=>8654320, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders_male[] = FixtureBuilder::build('posts', array('id'=>236, 'post_id'=>236, 'author_user_id'=>8654320, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 2.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 233)); + + $builders_male[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654321, 'network'=>'facebook')); + + $builders_male[] = FixtureBuilder::build('favorites', array('post_id'=>233, 'author_user_id'=>8654321, + 'fav_of_user_id'=>8654320, 'network'=>'facebook')); + + $builders_male[] = FixtureBuilder::build('users', array('user_id'=>8654321, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders_male[] = FixtureBuilder::build('users', array('user_id'=>8654320, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders_male; + + } + + private function buildDataForAll() { + $builders= array(); + + $now = date('Y-m-d H:i:s'); + $yesterday = date('Y-m-d H:i:s', strtotime('yesterday')); + + $builders[] = FixtureBuilder::build('posts', array('id'=>433, 'post_id'=>433, 'author_user_id'=>5654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple post.', + 'pub_date'=>$now, 'reply_count_cache'=>3, 'is_protected'=>0,'favlike_count_cache' => 2)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>434, 'post_id'=>434, 'author_user_id'=>5654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 433)); + + $builders[] = FixtureBuilder::build('posts', array('id'=>435, 'post_id'=>435, 'author_user_id'=>5654320, + 'author_username'=>'user1', 'author_fullname'=>'User1', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' => 0, + 'in_reply_to_post_id' => 433)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>433, 'author_user_id'=>5654321, + 'fav_of_user_id'=>5654321, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>433, 'author_user_id'=>5654321, + 'fav_of_user_id'=>5654320, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>5654321, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'female', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>5654320, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'male', 'avatar'=>'avatar.jpg', 'is_protected'=>0, + 'network'=>'facebook')); + + return $builders; + + } +} + diff --git a/webapp/plugins/insightsgenerator/tests/TestOfGeoAnalysisFacebookInsight.php b/webapp/plugins/insightsgenerator/tests/TestOfGeoAnalysisFacebookInsight.php new file mode 100644 index 0000000000..2f8af75bb8 --- /dev/null +++ b/webapp/plugins/insightsgenerator/tests/TestOfGeoAnalysisFacebookInsight.php @@ -0,0 +1,142 @@ +. + * + * + * GeoAnalysisFacebook (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ + +require_once dirname(__FILE__) . '/../../../../tests/init.tests.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/autorun.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/web_tester.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/insights/geoanalysisfacebook.php'; + +class TestOfGeoAnalysisFacebookInsight extends ThinkUpUnitTestCase { + + public function setUp() { + parent::setUp(); + } + + public function tearDown() { + parent::tearDown(); + } + + public function testGeoAnalysisFacebook() { + // Get data ready that insight requires + $builders = self::buildData(); + + $instance = new Instance(); + $instance->id = 220; + $instance->network_user_id = 19654321; + $instance->network_username = 'user'; + $instance->network = 'facebook'; + + $posts = array(); + $posts[] = new Post(array( + 'id'=>1333, + 'post_id'=>1333, + 'author_user_id'=>19654321, + 'author_username'=>'user', + 'author_fullname'=>'User', + 'network'=>'facebook', + 'post_text'=>'This is a simple post 1.', + 'pub_date'=>date('Y-m-d H:i:s'), + 'reply_count_cache'=>1, + 'is_protected'=>0, + 'favlike_count_cache' => 1 + )); + $posts[] = new Post(array( + 'id'=>2333, + 'post_id'=>2333, + 'author_user_id'=>19654321, + 'author_username'=>'user', + 'author_fullname'=>'User', + 'network'=>'facebook', + 'post_text'=>'This is a simple post 2.', + 'pub_date'=>date('Y-m-d H:i:s', strtotime('-1 day')), + 'reply_count_cache'=>1, + 'is_protected'=>0, + 'favlike_count_cache' => 1 + )); + $insight_plugin = new GeoAnalysisFacebookInsight(); + $insight_plugin->generateInsight($instance, $posts, 5); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('geo_analysis_facebook', 220, $today); + $geo_data = unserialize($result->related_data); + $this->debug(Utils::varDumpToString($result)); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'All over the world'); + $this->assertIsA($geo_data, "array"); + $this->assertEqual(count($geo_data), 1); + $this->assertEqual($geo_data[0][0]['city'], 'Kharkov'); + $this->assertEqual($geo_data[0][1]['city'], 'Chernigiv'); + + } + + private function buildData() { + $builders = array(); + + $builders[] = FixtureBuilder::build('posts', array('id'=>1334, 'post_id'=>1334, 'author_user_id'=>19654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 1.', + 'pub_date'=>$now, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 1333)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>1333, 'author_user_id'=>19654321, + 'fav_of_user_id'=>19654320, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>19654321, 'user_name'=>'user', + 'full_name'=>'User', 'gender'=>'male', 'location'=> 'Kharkov, Ukraine', 'avatar'=>'avatar.jpg', + 'is_protected'=>0, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>19654320, 'user_name'=>'user1', + 'full_name'=>'User1', 'gender'=>'female', 'location'=> 'Chernigiv', 'avatar'=>'avatar.jpg', + 'is_protected'=>0, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('posts', array('id'=>2334, 'post_id'=>2334, 'author_user_id'=>29654321, + 'author_username'=>'user', 'author_fullname'=>'User', 'author_avatar'=>'avatar.jpg', + 'network'=>'facebook', 'post_text'=>'This is a simple comment 2.', + 'pub_date'=>$yesterday, 'reply_count_cache'=>0, 'is_protected'=>0,'favlike_count_cache' =>0, + 'in_reply_to_post_id' => 2333)); + + $builders[] = FixtureBuilder::build('favorites', array('post_id'=>2333, 'author_user_id'=>19654321, + 'fav_of_user_id'=>29654321, 'network'=>'facebook')); + + $builders[] = FixtureBuilder::build('users', array('user_id'=>29654321, 'user_name'=>'user', + 'full_name'=>'User4', 'gender'=>'male', 'location'=> 'Chernigiv, Chernihivs\'Ka Oblast\', Ukraine', + 'avatar'=>'avatar.jpg', 'is_protected'=>0, 'network'=>'facebook')); + + return $builders; + + } +} + diff --git a/webapp/plugins/insightsgenerator/tests/TestOfGeoAnalysisTwitterInsight.php b/webapp/plugins/insightsgenerator/tests/TestOfGeoAnalysisTwitterInsight.php new file mode 100644 index 0000000000..a390ff43d4 --- /dev/null +++ b/webapp/plugins/insightsgenerator/tests/TestOfGeoAnalysisTwitterInsight.php @@ -0,0 +1,98 @@ +. + * + * + * GeoAnalysisTwitter (name of file) + * + * Description of what this class does + * + * Copyright (c) 2014 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2014 Anna Shkerina + */ + +require_once dirname(__FILE__) . '/../../../../tests/init.tests.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/autorun.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/web_tester.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/insights/geoanalysistwitter.php'; + +class TestOfGeoAnalysisTwitterInsight extends ThinkUpUnitTestCase { + + public function setUp() { + parent::setUp(); + } + + public function tearDown() { + parent::tearDown(); + } + + public function testGeoAnalysisTwitter() { + // Get data ready that insight requires + $builders = self::buildData(); + + $instance = new Instance(); + $instance->id = 1234; + $instance->network_user_id = 29654321; + $instance->network_username = 'testeriffic'; + $instance->network = 'twitter'; + + $insight_plugin = new GeoAnalysisTwitterInsight(); + $insight_plugin->generateInsight($instance, $builders, 4); + + // Assert that insight got inserted + $insight_dao = new InsightMySQLDAO(); + $today = date ('Y-m-d'); + $result = $insight_dao->getInsight('geo_analysis_twitter', 1234, $today); + $geo_data = unserialize($result->related_data); + $this->debug(Utils::varDumpToString($result)); + $this->assertNotNull($result); + $this->assertIsA($result, "Insight"); + $this->assertEqual($result->headline, 'Here am I!'); + $this->assertIsA($geo_data, "array"); + $this->assertEqual(count($geo_data[0]), 2); + $this->assertEqual($geo_data[0][0]['lat'], '34.7412'); + $this->assertEqual($geo_data[0][0]['long'], '51.6578'); + $this->assertEqual($geo_data[0][0]['place'], 'USA'); + $this->assertEqual($geo_data[0][1]['lat'], '51.5164'); + $this->assertEqual($geo_data[0][1]['long'], '31.3122'); + $this->assertEqual($geo_data[0][1]['place'], 'Ukraine'); + } + + private function buildData() { + $builders = array(); + + $builders[] = FixtureBuilder::build('posts', array('author_user_id'=>29654321, + 'network'=>'twitter', 'post_text'=>'This is a simple post.', + 'pub_date' => date('Y-m-d H:i:s', strtotime('-3 day')), 'geo'=>'34.7412563,51.6578233', + 'place'=>'USA')); + + $builders[] = FixtureBuilder::build('posts', array('author_user_id'=>29654321, + 'network'=>'twitter', 'post_text'=>'This is a simple post 1.', + 'pub_date' => date('Y-m-d H:i:s', strtotime('-2 day')), 'geo'=>'51.5164641,31.3122106', + 'place'=>'Ukraine')); + + return $builders; + + } +} + diff --git a/webapp/plugins/insightsgenerator/tests/TestOfTopOfPostsFacebookInsight.php b/webapp/plugins/insightsgenerator/tests/TestOfTopOfPostsFacebookInsight.php new file mode 100644 index 0000000000..e7fdcdc45e --- /dev/null +++ b/webapp/plugins/insightsgenerator/tests/TestOfTopOfPostsFacebookInsight.php @@ -0,0 +1,54 @@ +. + * + * + * TopOfPostsFacebook (name of file) + * + * Description of what this class does + * + * Copyright (c) 2013 Anna Shkerina + * + * @author Anna Shkerina blond00792@gmail.com + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2013 Anna Shkerina + */ + +require_once dirname(__FILE__) . '/../../../../tests/init.tests.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/autorun.php'; +require_once THINKUP_WEBAPP_PATH.'_lib/extlib/simpletest/web_tester.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/model/class.InsightPluginParent.php'; +require_once THINKUP_ROOT_PATH. 'webapp/plugins/insightsgenerator/insights/topofpostsfacebook.php'; + +class TestOfTopOfPostsFacebookInsight extends ThinkUpUnitTestCase { + + public function setUp(){ + parent::setUp(); + } + + public function tearDown() { + parent::tearDown(); + } + + public function testConstructor() { + $topofposts_insight_plugin = new TopOfPostsFacebookInsight(); + $this->assertIsA($topofposts_insight_plugin, 'TopOfPostsFacebookInsight' ); + } +} + diff --git a/webapp/plugins/insightsgenerator/view/_post.tpl b/webapp/plugins/insightsgenerator/view/_post.tpl index 1d3ca3b11c..73741185ee 100644 --- a/webapp/plugins/insightsgenerator/view/_post.tpl +++ b/webapp/plugins/insightsgenerator/view/_post.tpl @@ -56,6 +56,7 @@

{$post->author_fullname} {if $post->network == 'foursquare'}{$post->place}{/if} {if $post->other.total_likes}{$post->other.total_likes|number_format} likes{/if} + {if $post->shares_count_cache}{$post->shares_count_cache|number_format} shares{/if}

{$post->post_text} diff --git a/webapp/plugins/insightsgenerator/view/ageanalysis.tpl b/webapp/plugins/insightsgenerator/view/ageanalysis.tpl new file mode 100644 index 0000000000..d6bf55ae32 --- /dev/null +++ b/webapp/plugins/insightsgenerator/view/ageanalysis.tpl @@ -0,0 +1,69 @@ +{include file=$tpl_path|cat:'_header.tpl'} + +{if !$expand} +
+{/if} + + {$i->headline} + + +{$i->text|link_usernames_to_twitter} + +
+ {include file=$tpl_path|cat:"_post.tpl" post=$i->related_data[0] hide_insight_header=true} +
+ +{if !$expand} +
+{/if} + +
 
+ + +{if !$expand} +
+{/if} + +{include file=$tpl_path|cat:'_footer.tpl'} \ No newline at end of file diff --git a/webapp/plugins/insightsgenerator/view/genderanalysis.tpl b/webapp/plugins/insightsgenerator/view/genderanalysis.tpl new file mode 100644 index 0000000000..ef2429c8e0 --- /dev/null +++ b/webapp/plugins/insightsgenerator/view/genderanalysis.tpl @@ -0,0 +1,57 @@ +{include file=$tpl_path|cat:'_header.tpl'} + +{if !$expand} +
+{/if} + + {$i->headline} + + +{$i->text|link_usernames_to_twitter} + +
+ {include file=$tpl_path|cat:"_post.tpl" post=$i->related_data[0] hide_insight_header=true} +
+ +{if !$expand} +
+{/if} + +
 
+ + +{if !$expand} +
+{/if} + +{include file=$tpl_path|cat:'_footer.tpl'} diff --git a/webapp/plugins/insightsgenerator/view/geoanalysisfacebook.tpl b/webapp/plugins/insightsgenerator/view/geoanalysisfacebook.tpl new file mode 100644 index 0000000000..e09b8a0190 --- /dev/null +++ b/webapp/plugins/insightsgenerator/view/geoanalysisfacebook.tpl @@ -0,0 +1,55 @@ +{include file=$tpl_path|cat:'_header.tpl'} + +{if !$expand} +
+{/if} + + {$i->headline} + + +{$i->text|link_usernames_to_twitter} + +{if !$expand} +
+{/if} + +
 
+ + + +{if !$expand} +
+{/if} + +{include file=$tpl_path|cat:'_footer.tpl'} \ No newline at end of file diff --git a/webapp/plugins/insightsgenerator/view/geoanalysistwitter.tpl b/webapp/plugins/insightsgenerator/view/geoanalysistwitter.tpl new file mode 100644 index 0000000000..649e2ac1c7 --- /dev/null +++ b/webapp/plugins/insightsgenerator/view/geoanalysistwitter.tpl @@ -0,0 +1,56 @@ +{include file=$tpl_path|cat:'_header.tpl'} + +{if !$expand} +
+{/if} + + {$i->headline} + + +{$i->text|link_usernames_to_twitter} + +{if !$expand} +
+{/if} + +
 
+ + + +{if !$expand} +
+{/if} + +{include file=$tpl_path|cat:'_footer.tpl'} \ No newline at end of file diff --git a/webapp/plugins/insightsgenerator/view/topofpostsfacebook.tpl b/webapp/plugins/insightsgenerator/view/topofpostsfacebook.tpl new file mode 100644 index 0000000000..c4d300b4ae --- /dev/null +++ b/webapp/plugins/insightsgenerator/view/topofpostsfacebook.tpl @@ -0,0 +1,7 @@ +{include file=$tpl_path|cat:'_header.tpl'} + +
+ {include file=$tpl_path|cat:"_posts.tpl" icon="trophy"} +
+ +{include file=$tpl_path|cat:'_footer.tpl'} diff --git a/webapp/session/forgot.php b/webapp/session/forgot.php index 2cd5e4e88f..538a4b08d3 100644 --- a/webapp/session/forgot.php +++ b/webapp/session/forgot.php @@ -31,3 +31,4 @@ $controller = new ForgotPasswordController(); echo $controller->go(); +