Compare commits

..

25 commits

Author SHA1 Message Date
0f15cc65e0 composer update 2019-12-21 10:05:27 +00:00
f246a40d27
Test PHP 7.3 2019-02-19 13:48:12 +00:00
26751ae40c
Also check with lowest compatible versions 2019-02-19 13:40:59 +00:00
ee62ec9798 More spaces 2019-02-15 23:54:59 +00:00
0fb5afaf58 Merge branch 'test-factories' 2019-02-14 19:08:39 +00:00
c375390cbb Add and use CommentFactory in tests 2019-02-14 19:08:29 +00:00
d384398081 Require faker 2019-02-14 18:50:54 +00:00
41b308f1b6 Add talk titles to test comments 2019-02-14 13:49:58 +00:00
700bc784b1 Add URIs to test comments 2019-02-14 13:48:02 +00:00
ce3ce1e971 Format console output 2019-02-14 12:21:13 +00:00
aa3e6960a3 Extract getEventData method 2019-02-14 10:57:48 +00:00
fa56cf6908 Ensure that the winners are comments 2019-02-14 08:11:09 +00:00
4292cc3147 Add the Comment class 2019-02-14 07:52:16 +00:00
df9639e57c Add usage instructions 2019-02-14 03:07:04 +00:00
3e61221cf3
Add Travis badge 2019-02-14 03:03:34 +00:00
df80bdb0fb Update phpunit path 2019-02-14 02:57:55 +00:00
ac5a8323c6 Create .travis.yml 2019-02-14 02:51:31 +00:00
a564aa85fa Move parenthesis 2019-02-14 02:38:22 +00:00
2417088e80 Add TODOs 2019-02-14 02:36:55 +00:00
50d3fd71a6 Move cache to a property, update cache key 2019-02-14 02:31:37 +00:00
dcf53f8b0f Get and merge comments for each event 2019-02-14 02:31:16 +00:00
c030a9658d Use stdClass rather than arrays 2019-02-14 02:14:32 +00:00
119afd9f24 Require ext-json 2019-02-14 01:51:22 +00:00
40ef399b84 Create README.md 2019-02-14 01:35:58 +00:00
53e69067e4 Merge branch 'picker-service' 2019-02-14 01:34:55 +00:00
10 changed files with 622 additions and 447 deletions

17
.travis.yml Normal file
View file

@ -0,0 +1,17 @@
language: php
php:
- 7.1
- 7.2
- 7.3
env:
matrix:
- COMPOSER_FLAGS="--prefer-lowest"
- COMPOSER_FLAGS=""
before_script:
- composer install --dev --prefer-source --no-interaction
script:
- bin/phpunit

11
README.md Normal file
View file

@ -0,0 +1,11 @@
# Joind.in Winner Picker
[![Build Status](https://travis-ci.org/opdavies/joindin-winner-picker-new.svg?branch=master)](https://travis-ci.org/opdavies/joindin-winner-picker-new)
WIP
## Usage
```
bin/console app:pick-winner <tag>
```

View file

@ -5,6 +5,7 @@
"php": "^7.1.3", "php": "^7.1.3",
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*", "ext-iconv": "*",
"ext-json": "^1.5",
"guzzlehttp/guzzle": "^6.3", "guzzlehttp/guzzle": "^6.3",
"josephlavin/tap": "^1.0", "josephlavin/tap": "^1.0",
"symfony/cache": "4.2.*", "symfony/cache": "4.2.*",
@ -61,6 +62,7 @@
} }
}, },
"require-dev": { "require-dev": {
"fzaninotto/faker": "^1.8",
"symfony/maker-bundle": "^1.11", "symfony/maker-bundle": "^1.11",
"symfony/test-pack": "^1.0", "symfony/test-pack": "^1.0",
"symfony/var-dumper": "4.2.*" "symfony/var-dumper": "4.2.*"

649
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
namespace App\Command; namespace App\Command;
use App\Comment;
use App\Service\Picker; use App\Service\Picker;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use Symfony\Component\Cache\Simple\FilesystemCache; use Symfony\Component\Cache\Simple\FilesystemCache;
@ -10,6 +11,7 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Tightenco\Collect\Support\Collection;
class PickWinnerCommand extends Command class PickWinnerCommand extends Command
{ {
@ -20,6 +22,11 @@ class PickWinnerCommand extends Command
*/ */
private $client; private $client;
/**
* @var \Symfony\Contracts\Cache\CacheInterface
*/
private $cache;
/** /**
* @var \App\Service\Picker * @var \App\Service\Picker
*/ */
@ -33,8 +40,9 @@ class PickWinnerCommand extends Command
*/ */
public function __construct(Picker $picker) public function __construct(Picker $picker)
{ {
parent::__construct(); parent::__construct();
$this->client = new Client(); $this->client = new Client();
$this->cache = new FilesystemCache();
$this->picker = $picker; $this->picker = $picker;
} }
@ -58,31 +66,122 @@ class PickWinnerCommand extends Command
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$io->title('Joind.in Winner Picker!');
$tag = $input->getArgument('tag'); $tag = $input->getArgument('tag');
$startDate = (new \DateTime($input->getArgument('start')))->format('Y-m-d'); $startDate = (new \DateTime($input->getArgument('start')))->format('Y-m-d');
$endDate = (new \DateTime($input->getArgument('end')))->format('Y-m-d'); $endDate = (new \DateTime($input->getArgument('end')))->format('Y-m-d');
var_dump([ $io->comment(vsprintf('Selecting from #%s events between %s and %s.', [
'tag' => $tag, $tag,
'start date' => $startDate, $startDate,
'end date' => $endDate, $endDate,
]); ]));
// $io->success('You have a new command! Now make it your own! Pass --help to see your options.'); $events = collect($this->getEventData($tag, $startDate, $endDate));
$cache = new FilesystemCache(); $this->picker->setHosts($events);
$cacheKey = md5(collect([$tag, $startDate, $endDate])->implode('_')); $this->picker->setComments($this->allComments($events));
if (!$events = $cache->get($cacheKey)) { $this->picker->getWinners(1)->each(function (Comment $comment) use ($io) {
$io->section(vsprintf('%s (%s)', [
$comment->getUserDisplayName(),
$comment->getTalkTitle(),
]));
$io->text($comment->getText());
$io->text($comment->getUri());
});
}
/**
* Get all comments (talks and event) for the events.
*
* @param \Tightenco\Collect\Support\Collection $events
* The retrieved events.
*
* @return \Tightenco\Collect\Support\Collection
* The merged comments.
*/
private function allComments(Collection $events): Collection
{
return $events->map(function (\stdClass $event) {
return $this
->eventComments($event)
->merge($this->talkComments($event))
;
});
}
/**
* Get the event comments.
*
* @param \stdClass $event
* The event.
*
* @return \Tightenco\Collect\Support\Collection
*/
private function eventComments(\stdClass $event): Collection
{
// TODO: Cache this.
$response = $this->client->get(
$event->comments_uri,
['query' => ['resultsperpage' => 1000]]
)->getBody();
return collect(json_decode($response)->comments);
}
/**
* Get the talk comments.
*
* @param \stdClass $event
* The event.
*
* @return \Tightenco\Collect\Support\Collection
*/
private function talkComments(\stdClass $event): Collection
{
// TODO: Cache this.
$response = $this->client->get(
$event->all_talk_comments_uri,
['query' => ['resultsperpage' => 1000]]
)->getBody();
return collect(json_decode($response)->comments);
}
/**
* Get the event data.
*
* @param string $tag
* The tag to search for.
* @param string $startDate
* The start date limit.
* @param string $endDate
* The end date limit.
*
* @return array
*
* @throws \Psr\Cache\InvalidArgumentException
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
private function getEventData(string $tag, string $startDate, string $endDate): array
{
$cacheKey = md5(collect(['events:', $tag, $startDate, $endDate])->implode('_'));
if (!$events = $this->cache->get($cacheKey)) {
$response = $this->client->get('http://api.joind.in/v2.1/events', [ $response = $this->client->get('http://api.joind.in/v2.1/events', [
'query' => [ 'query' => [
'tags' => [$tag], 'tags' => [$tag],
'startdate' => $startDate, 'startdate' => $startDate,
'enddate' => $endDate, 'enddate' => $endDate,
'verbose' => 'yes', 'verbose' => 'yes',
] ],
]); ]);
$cache->set($cacheKey, json_decode($response->getBody())->events, 3600); $events = json_decode($response->getBody())->events;
$this->cache->set($cacheKey, $events, 3600);
} }
return $events;
} }
} }

106
src/Comment.php Normal file
View file

@ -0,0 +1,106 @@
<?php
namespace App;
class Comment
{
/**
* @var string
*/
private $text;
/**
* @var string
*/
private $userDisplayName;
/**
* The URI for the comment.
*
* @var string
*/
private $uri;
/**
* The title of the talk that was commented on.
*
* @var string
*/
private $talkTitle;
/**
* Set the comment text.
*
* @param string $text
*/
public function setText(string $text): void
{
$this->text = $text;
}
/**
* Set the user's display name.
*
* @param string $name
*/
public function setUserDisplayName(string $name): void
{
$this->userDisplayName = $name;
}
/**
* Set the URI for the comment.
*
* @param string $uri
*/
public function setUri(string $uri): void
{
$this->uri = $uri;
}
/**
* Set the talk title.
*
* @param string $talkTitle
*/
public function setTalkTitle(string $talkTitle): void
{
$this->talkTitle = $talkTitle;
}
/**
* @return string
*/
public function getText(): string
{
return $this->text;
}
/**
* @return mixed
*/
public function getUserDisplayName(): string
{
return $this->userDisplayName;
}
/**
* Get the URI for the comment.
*
* @return string
*/
public function getUri(): string
{
return $this->uri;
}
/**
* Get the talk title.
*
* @return string
*/
public function getTalkTitle(): string
{
return $this->talkTitle;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Service; namespace App\Service;
use App\Comment;
use Tightenco\Collect\Support\Collection; use Tightenco\Collect\Support\Collection;
class Picker class Picker
@ -77,8 +78,16 @@ class Picker
{ {
$this->comments = $comments $this->comments = $comments
->flatten(1) ->flatten(1)
->filter(function (array $comment) { ->filter(function (\stdClass $comment) {
return !$this->isUserAnEventHost($comment['user_display_name']); return !$this->isUserAnEventHost($comment->user_display_name);
})
->map(function (\stdClass $original) {
return tap(new Comment(), function (Comment $comment) use ($original) {
$comment->setText($original->comment);
$comment->setUserDisplayName($original->user_display_name);
$comment->setUri($original->uri);
$comment->setTalkTitle($original->talk_title);
});
}) })
->values(); ->values();

View file

@ -5,6 +5,9 @@
"facebook/webdriver": { "facebook/webdriver": {
"version": "1.6.0" "version": "1.6.0"
}, },
"fzaninotto/faker": {
"version": "v1.8.0"
},
"guzzlehttp/guzzle": { "guzzlehttp/guzzle": {
"version": "6.3.3" "version": "6.3.3"
}, },
@ -20,6 +23,9 @@
"nikic/php-parser": { "nikic/php-parser": {
"version": "v4.2.0" "version": "v4.2.0"
}, },
"php": {
"version": "7.3"
},
"psr/cache": { "psr/cache": {
"version": "1.0.1" "version": "1.0.1"
}, },

View file

@ -0,0 +1,68 @@
<?php
namespace App\Tests\Helpers\Factory;
use App\Comment;
use Faker\Factory;
use Tightenco\Collect\Support\Collection;
class CommentFactory
{
/**
* @var \Faker\Factory
*/
private $faker;
/**
* The number of comments to create.
*
* @var int
*/
private $commentCount = 0;
/**
* CommentFactory constructor.
*/
public function __construct()
{
$this->faker = Factory::create();
}
/**
* Create new comments.
*
* @return \Tightenco\Collect\Support\Collection
*/
public function create(): Collection
{
$talkTitle = $this->faker->sentence;
return tap(collect(), function (Collection $data) use ($talkTitle) {
if ($this->commentCount > 0) {
foreach (range(1, $this->commentCount) as $i) {
$comment = new \stdClass();
$comment->talk_title = $talkTitle;
$comment->comment = $this->faker->paragraph;
$comment->uri = 'http://api.joind.in/v2.1/talk_comments/' . $this->faker->randomNumber(8);
$comment->user_display_name = $this->faker->name;
$data->push($comment);
}
}
});
}
/**
* Set the number of comments to create.
*
* @param int $count
*
* @return \App\Tests\Helpers\Factory\CommentFactory
*/
public function setCount(int $count): self
{
$this->commentCount = $count;
return $this;
}
}

View file

@ -2,7 +2,9 @@
namespace App\Tests\Service; namespace App\Tests\Service;
use App\Comment;
use App\Service\Picker; use App\Service\Picker;
use App\Tests\Helpers\Factory\CommentFactory;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Tightenco\Collect\Support\Collection; use Tightenco\Collect\Support\Collection;
@ -41,22 +43,8 @@ class PickerTest extends TestCase
public function comments_for_multiple_events_are_flattened_and_combined() public function comments_for_multiple_events_are_flattened_and_combined()
{ {
$data = [ $data = [
[ (new CommentFactory())->setCount(2)->create(),
[ (new CommentFactory())->setCount(1)->create(),
'comment' => 'Great talk!',
'user_display_name' => 'Dan Ackroyd',
],
[
'comment' => 'Could be better.',
'user_display_name' => 'Lucia Velasco',
],
],
[
[
'comment' => 'Needs more cat pictures.',
'user_display_name' => 'Rupert Jabelman',
],
],
]; ];
$comments = (new Picker()) $comments = (new Picker())
@ -70,58 +58,29 @@ class PickerTest extends TestCase
/** @test */ /** @test */
public function comments_from_event_hosts_cannot_be_picked() public function comments_from_event_hosts_cannot_be_picked()
{ {
$comments = (new CommentFactory())->setCount(3)->create();
$event = [ $event = [
'hosts' => [ 'hosts' => [
['host_name' => 'Oliver Davies'], ['host_name' => $hostName = $comments[1]->user_display_name],
], ],
]; ];
$comments = [ /** @var \Tightenco\Collect\Support\Collection $userNames */
[ $userNames = (new Picker())
[
'comment' => 'Great talk!',
'user_display_name' => 'Peter Fisher',
],
[
'comment' => 'Text on slides could be bigger.',
'user_display_name' => 'Oliver Davies',
],
[
'comment' => 'Speak slower.',
'user_display_name' => 'Zan Baldwin',
],
],
];
$comments = (new Picker())
->setHosts(collect([$event])) ->setHosts(collect([$event]))
->setComments(collect($comments)) ->setComments(collect($comments))
->getComments(); ->getComments()
->map->getUserDisplayName();
$this->assertCount(2, $comments); $this->assertCount(2, $userNames);
$this->assertSame('Peter Fisher', $comments[0]['user_display_name']); $this->assertFalse($userNames->contains($hostName));
$this->assertSame('Zan Baldwin', $comments[1]['user_display_name']);
} }
/** @test */ /** @test */
public function winners_can_be_selected() public function winners_can_be_selected()
{ {
$comments = [ $comments = (new CommentFactory())->setCount(3)->create();
[
[
'comment' => 'Great talk!',
'user_display_name' => 'Peter Fisher',
],
[
'comment' => 'Text on slides could be bigger.',
'user_display_name' => 'Michael Bush',
],
[
'comment' => 'Speak slower.',
'user_display_name' => 'Zan Baldwin',
],
],
];
$picker = new Picker(); $picker = new Picker();
$picker->setComments(collect($comments)); $picker->setComments(collect($comments));
@ -129,12 +88,17 @@ class PickerTest extends TestCase
tap($picker->getWinners(1), function (Collection $winners) use ($picker) { tap($picker->getWinners(1), function (Collection $winners) use ($picker) {
$this->assertCount(1, $winners); $this->assertCount(1, $winners);
$this->assertInstanceOf(Comment::class, $winners->first());
$this->assertTrue($picker->getComments()->contains($winners->first())); $this->assertTrue($picker->getComments()->contains($winners->first()));
}); });
tap($picker->getWinners(2), function (Collection $winners) use ($picker) { tap($picker->getWinners(2), function (Collection $winners) use ($picker) {
$this->assertCount(2, $winners); $this->assertCount(2, $winners);
$this->assertInstanceOf(Comment::class, $winners->first());
$this->assertTrue($picker->getComments()->contains($winners->first())); $this->assertTrue($picker->getComments()->contains($winners->first()));
$this->assertInstanceOf(Comment::class, $winners->last());
$this->assertTrue($picker->getComments()->contains($winners->last())); $this->assertTrue($picker->getComments()->contains($winners->last()));
}); });
} }