Skip to content

Commit

Permalink
fix(kobo): Support reading state with page number
Browse files Browse the repository at this point in the history
  • Loading branch information
ragusa87 committed May 25, 2024
1 parent a1b7c98 commit 9fd3f20
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 7 deletions.
16 changes: 16 additions & 0 deletions src/Controller/KoboStateController.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,29 @@ public function state(Kobo $kobo, string $uuid, Request $request): Response|Json
if ($interaction === false) {
$interaction = new BookInteraction();
$interaction->setBook($book);
$interaction->setReadPages(0); // On a new interaction, we assume the user has read 0 pages
$interaction->setUser($kobo->getUser());
$interactions->add($interaction);
$this->em->persist($interaction);
}

$interaction->setUpdated($state->lastModified);
$interaction->setFinished($state->statusInfo?->status === ReadingStateStatusInfo::STATUS_FINISHED);
switch ($state->statusInfo?->status) {
case ReadingStateStatusInfo::STATUS_FINISHED:
$interaction->setReadPages($book->getPageNumber());

break;
case ReadingStateStatusInfo::STATUS_READING:
$percent = $state->currentBookmark?->progressPercent;
$numPages = $percent !== null && $book->getPageNumber() !== null ? $book->getPageNumber() * $percent / 100 : null;
if ($numPages !== null) {
$interaction->setReadPages((int) $numPages);
}
break;
case null:
break;
}
$this->em->flush();

return new StateResponse($book);
Expand Down
1 change: 1 addition & 0 deletions src/DataFixtures/BookFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
class BookFixture extends Fixture implements DependentFixtureInterface
{
public const BOOK_REFERENCE = 'book-odyssey';
public const BOOK_PAGE_REFERENCE = '7557680347007504212_1727-h-21.htm.xhtml';
public const ID = 1;
public const UUID = '54c8fb05-cf05-4cb6-9482-bc25fa49fa80';

Expand Down
2 changes: 1 addition & 1 deletion src/Kobo/Request/Bookmark.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Bookmark
{
public ?int $contentSourceProgressPercent = null;
public ?\DateTime $lastModified = null;
public mixed $location;
public ?ReadingStateLocation $location = null;

public ?int $progressPercent = null;
}
2 changes: 1 addition & 1 deletion src/Kobo/Request/ReadingState.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ class ReadingState
public ?Bookmark $currentBookmark = null;
public ?string $entitlementId = null;
public ?\DateTimeImmutable $lastModified = null;
public mixed $statistics = null;
public ?ReadingStateStatistics $statistics = null;
public ?ReadingStateStatusInfo $statusInfo = null;
}
13 changes: 13 additions & 0 deletions src/Kobo/Request/ReadingStateLocation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App\Kobo\Request;

class ReadingStateLocation
{
public const TYPE_KOBO_SPAN = 'KoboSpan';
public const TYPE_KOBO_VALUE = 'kobo.1.1';

public ?string $source = null;
public ?string $type = self::TYPE_KOBO_SPAN;
public ?string $value = self::TYPE_KOBO_VALUE;
}
10 changes: 10 additions & 0 deletions src/Kobo/Request/ReadingStateStatistics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Kobo\Request;

class ReadingStateStatistics
{
public ?\DateTimeImmutable $lastModified = null;
public ?int $remainingTimeMinutes = null;
public ?int $spentReadingMinutes = null;
}
2 changes: 1 addition & 1 deletion src/Kobo/Request/ReadingStateStatusInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ReadingStateStatusInfo
public const STATUS_READING = 'Reading';
public const STATUS_FINISHED = 'Finished';

public ?\DateTime $lastModified = null;
public ?\DateTimeImmutable $lastModified = null;

public ?string $status = null;
}
7 changes: 7 additions & 0 deletions src/Kobo/Request/ReadingStates.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

class ReadingStates
{
/**
* @param array<int, ReadingState> $readingState
*/
public function __construct(array $readingState = [])
{
$this->readingStates = $readingState;
}
/** @var array<int, ReadingState> */
public array $readingStates = [];
}
1 change: 1 addition & 0 deletions tests/Controller/AbstractKoboControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ protected function getEntityManager(): EntityManagerInterface{

protected function setUp(): void
{
parent::setUp();
self::createClient();

$this->kobo = $this->loadKobo();
Expand Down
95 changes: 91 additions & 4 deletions tests/Controller/KoboStateControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@
namespace App\Tests\Controller;

use App\DataFixtures\BookFixture;
use App\DataFixtures\ShelfFixture;
use App\Entity\Book;
use App\Entity\Shelf;
use Doctrine\ORM\EntityManager;
use App\Entity\BookInteraction;
use App\Kobo\Request\Bookmark;
use App\Kobo\Request\ReadingState;
use App\Kobo\Request\ReadingStateLocation;
use App\Kobo\Request\ReadingStates;
use App\Kobo\Request\ReadingStateStatistics;
use App\Kobo\Request\ReadingStateStatusInfo;
use Symfony\Component\Serializer\SerializerInterface;

class KoboStateControllerTest extends AbstractKoboControllerTest
/**
* @phpstan-type ReadingStateCriteria array{'book':int, 'readPages': int, 'finished': boolean}
*/
class KoboStateControllerTest extends AbstractKoboControllerTest
{
public function testOpen() : void
{
Expand All @@ -24,6 +32,28 @@ public function testOpen() : void
self::assertResponseHeaderSame('Connection', 'keep-alive');
}

/**
* @dataProvider readingStatesProvider
* @param ReadingStateCriteria $criteria
*/
public function testPutState(int $bookId, ReadingStates $readingStates, array $criteria) : void
{
$client = static::getClient();
$serializer = $this->getSerializer();

$book = $this->getBookById($bookId);
self::assertNotNull($book, 'Book '.$bookId.' not found');
self::assertNotNull($book->getUuid(), 'Book '.$bookId.' has no UUID');

$json = $serializer->serialize($readingStates, 'json');
$client?->request('PUT', sprintf('/kobo/%s/v1/library/%s/state', $this->accessKey, $book->getUuid()), [],[],[] , $json);

self::assertResponseIsSuccessful();

$interaction = $this->getEntityManager()->getRepository(BookInteraction::class)->findOneBy($criteria);
self::assertNotNull($interaction, 'No Interaction found matching your criteria');
}

protected function getBookById(int $id): ?Book
{
$book = $this->getEntityManager()->getRepository(Book::class)->findOneBy(['id' => $id]);
Expand All @@ -32,5 +62,62 @@ protected function getBookById(int $id): ?Book
return $book;
}

private function getSerializer(): SerializerInterface
{
$service = self::getContainer()->get('serializer');
assert($service instanceof SerializerInterface);

return $service;
}

private function getReadingStates(string $bookUuid, int $percent = 50): ReadingStates
{
assert($percent >= 0 && $percent <= 100, 'Percent must be between 0 and 100');

$state = new ReadingState();
$state->lastModified = new \DateTimeImmutable();
$state->currentBookmark = new Bookmark();
$state->currentBookmark->contentSourceProgressPercent = $state->currentBookmark->progressPercent = $percent;
$state->currentBookmark->location = new ReadingStateLocation();
$state->currentBookmark->location->source = BookFixture::BOOK_PAGE_REFERENCE;
$state->currentBookmark->lastModified = new \DateTime();
$state->entitlementId = $bookUuid;
$state->statusInfo = new ReadingStateStatusInfo();
$state->statusInfo->status = $percent === 100 ? ReadingStateStatusInfo::STATUS_FINISHED : ReadingStateStatusInfo::STATUS_READING;
$state->statusInfo->lastModified = $state->lastModified;
$state->statistics = new ReadingStateStatistics();
$state->statistics->remainingTimeMinutes = 100 * ($percent/100);
$state->statistics->spentReadingMinutes = 100 - $state->statistics->remainingTimeMinutes;
$state->statistics->lastModified = $state->lastModified;

return new ReadingStates([$state]);
}

/**
* @return array<array{0: int, 1: ReadingStates, 2: ReadingStateCriteria}>
*/
public function readingStatesProvider(): array
{
return [
[
BookFixture::ID,
$this->getReadingStates(BookFixture::UUID, 50),
[
'book' => BookFixture::ID,
'readPages' => 15,
'finished' => false,
]
],
[
BookFixture::ID,
$this->getReadingStates(BookFixture::UUID, 100),
[
'book' => BookFixture::ID,
'readPages' => 30,
'finished' => true,
]
],
];
}

}

0 comments on commit 9fd3f20

Please sign in to comment.