Skip to content

Commit

Permalink
Merge pull request #464 from leepeuker/import-and-export-watchlist
Browse files Browse the repository at this point in the history
Added the ability to import and export watchlist
  • Loading branch information
leepeuker committed Aug 6, 2023
2 parents b650734 + 73b22a5 commit 490ee93
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 4 deletions.
5 changes: 5 additions & 0 deletions src/Domain/Movie/Watchlist/MovieWatchlistApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public function addMovieToWatchlist(int $userId, int $movieId, ?DateTime $addedA
$this->repository->addMovieToWatchlist($userId, $movieId, $addedAt);
}

public function fetchAllWatchlistItems(int $userId) : ?array
{
return $this->repository->fetchAllWatchlistItems($userId);
}

public function fetchWatchlistCount(int $userId, ?string $searchTerm = null) : int
{
return $this->repository->fetchWatchlistCount($userId, $searchTerm);
Expand Down
7 changes: 7 additions & 0 deletions src/Domain/Movie/Watchlist/MovieWatchlistRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ public function addMovieToWatchlist(int $userId, int $movieId, ?DateTime $addedA
);
}

public function fetchAllWatchlistItems(int $userId) : ?array
{
return $this->dbConnection->fetchAllAssociative(
'SELECT title, tmdb_id, imdb_id, added_at FROM watchlist JOIN movie m ON movie_id = m.id WHERE user_id = ?', [$userId],
);
}

public function fetchWatchlistCount(int $userId, ?string $searchTerm) : int
{
if ($searchTerm !== null) {
Expand Down
2 changes: 2 additions & 0 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Movary\Command;
use Movary\Command\CreatePublicStorageLink;
use Movary\Domain\Movie\MovieApi;
use Movary\Domain\Movie\Watchlist\MovieWatchlistApi;
use Movary\Domain\User;
use Movary\Domain\User\Service\Authentication;
use Movary\Domain\User\UserApi;
Expand Down Expand Up @@ -168,6 +169,7 @@ public static function createExportService(ContainerInterface $container) : Expo
{
return new ExportService(
$container->get(MovieApi::class),
$container->get(MovieWatchlistApi::class),
$container->get(ExportWriter::class),
self::createDirectoryStorage(),
);
Expand Down
1 change: 1 addition & 0 deletions src/HttpController/ExportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function getCsvExport(Request $request) : Response
$exportCsvPath = match ($exportType) {
'history' => $this->exportService->createExportHistoryCsv($userId),
'ratings' => $this->exportService->createExportRatingsCsv($userId),
'watchlist' => $this->exportService->createExportWatchlistCsv($userId),
default => null
};

Expand Down
12 changes: 12 additions & 0 deletions src/HttpController/ImportController.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function handleCsvImport(Request $request) : Response
match ($exportType) {
'history' => $this->importHistory($userId, $fileParameters),
'ratings' => $this->importRatings($userId, $fileParameters),
'watchlist' => $this->importWatchlist($userId, $fileParameters),
default => throw new RuntimeException('Export type not handled: ' . $exportType)
};
} catch (Throwable $t) {
Expand Down Expand Up @@ -72,4 +73,15 @@ private function importRatings(int $userId, array $fileParameter) : void

$this->sessionWrapper->set('importRatingsSuccessful', true);
}

private function importWatchlist(int $userId, array $fileParameter) : void
{
if (empty($fileParameter['watchlist']['tmp_name']) === true) {
throw new RuntimeException('Import csv file missing');
}

$this->importService->importWatchlist($userId, $fileParameter['watchlist']['tmp_name']);

$this->sessionWrapper->set('importWatchlistSuccessful', true);
}
}
3 changes: 3 additions & 0 deletions src/HttpController/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,15 @@ public function renderDataAccountPage() : Response

$importHistorySuccessful = $this->sessionWrapper->find('importHistorySuccessful');
$importRatingsSuccessful = $this->sessionWrapper->find('importRatingsSuccessful');
$importWatchlistSuccessful = $this->sessionWrapper->find('importWatchlistSuccessful');
$importHistoryError = $this->sessionWrapper->find('importHistoryError');
$deletedUserHistory = $this->sessionWrapper->find('deletedUserHistory');
$deletedUserRatings = $this->sessionWrapper->find('deletedUserRatings');

$this->sessionWrapper->unset(
'importHistorySuccessful',
'importRatingsSuccessful',
'importWatchlistSuccessful',
'importHistoryError',
'deletedUserHistory',
'deletedUserRatings',
Expand All @@ -205,6 +207,7 @@ public function renderDataAccountPage() : Response
'coreAccountChangesDisabled' => $user->hasCoreAccountChangesDisabled(),
'importHistorySuccessful' => $importHistorySuccessful,
'importRatingsSuccessful' => $importRatingsSuccessful,
'importWatchlistSuccessful' => $importWatchlistSuccessful,
'importHistoryError' => $importHistoryError,
'deletedUserHistory' => $deletedUserHistory,
'deletedUserRatings' => $deletedUserRatings,
Expand Down
29 changes: 29 additions & 0 deletions src/Service/Export/ExportService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
namespace Movary\Service\Export;

use Movary\Domain\Movie\MovieApi;
use Movary\Domain\Movie\Watchlist\MovieWatchlistApi;

class ExportService
{
private const CSV_HEADER_HISTORY = 'title,year,tmdbId,imdbId,watchedAt,comment' . PHP_EOL;

private const CSV_HEADER_RATINGS = 'title,year,tmdbId,imdbId,userRating' . PHP_EOL;

private const CSV_HEADER_WATCHLIST = 'title,tmdbId,imdbId,addedAt' . PHP_EOL;

public function __construct(
private readonly MovieApi $movieApi,
private readonly MovieWatchlistApi $watchlistApi,
private readonly ExportWriter $dataMapper,
private readonly string $storageDirectory,
) {
Expand Down Expand Up @@ -59,6 +63,31 @@ public function createExportRatingsCsv(int $userId, ?string $fileName = null) :
return $fileName;
}

public function createExportWatchlistCsv(int $userId, ?string $fileName = null) : ?string
{
$watchlist = $this->watchlistApi->fetchAllWatchlistItems($userId);

if($fileName === null) {
$fileName = $this->generateFilename($userId, 'watchlist');
}

if($watchlist === null) {
return null;
}

$exportFileHandle = $this->createFileHandle($fileName);

fwrite($exportFileHandle, self::CSV_HEADER_WATCHLIST);

foreach ($watchlist as $watchlistItem) {
$this->dataMapper->writeWatchlistItemToCsv($exportFileHandle, $watchlistItem);
}

fclose($exportFileHandle);

return $fileName;
}

/** @return resource */
private function createFileHandle(string $fileName)
{
Expand Down
14 changes: 14 additions & 0 deletions src/Service/Export/ExportWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ public function writeWatchDateToCsv($fileHandle, array $movieWatchDate) : void
}
}
}

/** @param resource $fileHandle */
public function writeWatchlistItemToCsv($fileHandle, array $watchlistItem) : void
{
$lengthOfWrittenString = fputcsv($fileHandle, [
$watchlistItem['title'],
$watchlistItem['tmdb_id'],
$watchlistItem['imdb_id'],
$watchlistItem['added_at'],
]);
if ($lengthOfWrittenString === false) {
throw new \RuntimeException('Could not write watch date to export csv');
}
}

private function convertReleaseDate(?string $movieWatchDate) : ?Year
{
Expand Down
24 changes: 22 additions & 2 deletions src/Service/ImportService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
use League\Csv\Reader;
use Movary\Domain\Movie\MovieApi;
use Movary\Domain\Movie\MovieEntity;
use Movary\Domain\Movie\Watchlist\MovieWatchlistApi;
use Movary\ValueObject\Date;
use Movary\ValueObject\DateTime;
use Movary\ValueObject\PersonalRating;
use RuntimeException;

class ImportService
{
public function __construct(private readonly MovieApi $movieApi)
{
public function __construct(
private readonly MovieApi $movieApi,
private readonly MovieWatchlistApi $watchlistApi,
) {
}

public function findOrCreateMovie(int $tmdbId, string $title, ?string $imdbId) : MovieEntity
Expand Down Expand Up @@ -69,4 +73,20 @@ public function importRatings(int $userId, string $importCsvPath) : void
$this->movieApi->updateUserRating($movie->getId(), $userId, PersonalRating::create((int)$record['userRating']));
}
}

public function importWatchlist(int $userId, string $importCsvPath) : void
{
$csv = Reader::createFromPath($importCsvPath, 'r');
$csv->setHeaderOffset(0);

foreach ($csv->getRecords() as $record) {
if (isset($record['title'], $record['tmdbId'], $record['imdbId'], $record['addedAt']) === false) {
throw new RuntimeException('Import csv is missing data');
}

$movie = $this->findOrCreateMovie((int)$record['tmdbId'], (string)$record['title'], (string)$record['imdbId']);

$this->watchlistApi->addMovieToWatchlist($userId, $movie->getId(), DateTime::createFromString((string)$record['addedAt']));
}
}
}
26 changes: 24 additions & 2 deletions templates/page/settings-account-data.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
<div style="padding-top: 1rem">
<h5>Export & Import</h5>

<p class="text-muted">Export or import your movary history or ratings.</p>
<p class="text-muted">Export or import your movary history, ratings or watchlist.</p>

<a class="btn btn-primary" target="_blank" href="/settings/account/export/csv/history">Export: history.csv</a>
<a class="btn btn-primary" target="_blank" href="/settings/account/export/csv/ratings">Export: ratings.csv</a>
<a class="btn btn-primary" target="_blank" href="/settings/account/export/csv/watchlist">Export: watchlist.csv</a>

<form action="/settings/account/import/csv/history" method="post" enctype="multipart/form-data" style="margin-top: 1.2rem">
<div class="input-group">
Expand All @@ -40,7 +41,7 @@
</div>
{% endif %}

<form action="/settings/account/import/csv/ratings" method="post" enctype="multipart/form-data" style="padding-bottom: 0.2rem;margin-top: 1rem">
<form action="/settings/account/import/csv/ratings" method="post" enctype="multipart/form-data" style="margin-top: 1rem">
<div class="input-group">
<input type="file" class="form-control" name="ratings" required {% if coreAccountChangesDisabled == true %}disabled{% endif %}>
<button class="btn btn-primary" type="submit" {% if coreAccountChangesDisabled == true %}disabled{% endif %}>Import rating.csv</button>
Expand All @@ -60,6 +61,27 @@
</div>
{% endif %}

<form action="/settings/account/import/csv/watchlist" method="post" enctype="multipart/form-data" style="padding-bottom: 0.2rem;margin-top: 1rem">
<div class="input-group">
<input type="file" class="form-control" name="watchlist" required {% if coreAccountChangesDisabled == true %}disabled{% endif %}>
<button class="btn btn-primary" type="submit" {% if coreAccountChangesDisabled == true %}disabled{% endif %}>Import watchlist.csv</button>
</div>
</form>

{% if importWatchlistSuccessful == true %}
<div class="alert alert-success alert-dismissible" role="alert" style="margin-left: 5%;margin-right: 5%;margin-bottom: 0.7rem!important;margin-top: 1rem">
Watchlist was successfully imported.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}

{% if importHistoryError == 'watchlist' %}
<div class="alert alert-danger alert-dismissible" role="alert" style="margin-left: 5%;margin-right: 5%;margin-bottom: 0.7rem!important;margin-top: 1rem">
Watchlist could not be imported.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}

<hr>

<h5>Delete your data</h5>
Expand Down

0 comments on commit 490ee93

Please sign in to comment.