Compare commits

..

No commits in common. "master" and "picker-service" have entirely different histories.

10 changed files with 451 additions and 626 deletions

View file

@ -1,17 +0,0 @@
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

View file

@ -1,11 +0,0 @@
# 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,7 +5,6 @@
"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.*",
@ -62,7 +61,6 @@
} }
}, },
"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.*"

657
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,6 @@
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;
@ -11,7 +10,6 @@ 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
{ {
@ -22,11 +20,6 @@ class PickWinnerCommand extends Command
*/ */
private $client; private $client;
/**
* @var \Symfony\Contracts\Cache\CacheInterface
*/
private $cache;
/** /**
* @var \App\Service\Picker * @var \App\Service\Picker
*/ */
@ -40,9 +33,8 @@ 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;
} }
@ -66,122 +58,31 @@ 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');
$io->comment(vsprintf('Selecting from #%s events between %s and %s.', [ var_dump([
$tag, 'tag' => $tag,
$startDate, 'start date' => $startDate,
$endDate, 'end date' => $endDate,
])); ]);
$events = collect($this->getEventData($tag, $startDate, $endDate)); // $io->success('You have a new command! Now make it your own! Pass --help to see your options.');
$this->picker->setHosts($events); $cache = new FilesystemCache();
$this->picker->setComments($this->allComments($events)); $cacheKey = md5(collect([$tag, $startDate, $endDate])->implode('_'));
$this->picker->getWinners(1)->each(function (Comment $comment) use ($io) { if (!$events = $cache->get($cacheKey)) {
$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',
], ]
]); ]);
$events = json_decode($response->getBody())->events; $cache->set($cacheKey, json_decode($response->getBody())->events, 3600);
$this->cache->set($cacheKey, $events, 3600);
} }
return $events;
} }
} }

View file

@ -1,106 +0,0 @@
<?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,7 +2,6 @@
namespace App\Service; namespace App\Service;
use App\Comment;
use Tightenco\Collect\Support\Collection; use Tightenco\Collect\Support\Collection;
class Picker class Picker
@ -78,16 +77,8 @@ class Picker
{ {
$this->comments = $comments $this->comments = $comments
->flatten(1) ->flatten(1)
->filter(function (\stdClass $comment) { ->filter(function (array $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,9 +5,6 @@
"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"
}, },
@ -23,9 +20,6 @@
"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

@ -1,68 +0,0 @@
<?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,9 +2,7 @@
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;
@ -43,8 +41,22 @@ 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())
@ -58,29 +70,58 @@ 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' => $hostName = $comments[1]->user_display_name], ['host_name' => 'Oliver Davies'],
], ],
]; ];
/** @var \Tightenco\Collect\Support\Collection $userNames */ $comments = [
$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, $userNames); $this->assertCount(2, $comments);
$this->assertFalse($userNames->contains($hostName)); $this->assertSame('Peter Fisher', $comments[0]['user_display_name']);
$this->assertSame('Zan Baldwin', $comments[1]['user_display_name']);
} }
/** @test */ /** @test */
public function winners_can_be_selected() public function winners_can_be_selected()
{ {
$comments = (new CommentFactory())->setCount(3)->create(); $comments = [
[
[
'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));
@ -88,17 +129,12 @@ 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()));
}); });
} }