feat: Add fetching of SentenceListening and account for them on command
This commit is contained in:
parent
96963fb926
commit
7333c5164d
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Command;
|
namespace App\Command;
|
||||||
|
|
||||||
|
use App\Entity\SentenceListeningNote;
|
||||||
use App\Entity\SentenceNote;
|
use App\Entity\SentenceNote;
|
||||||
use App\Entity\Term;
|
use App\Entity\Term;
|
||||||
use App\Service\AnkiService;
|
use App\Service\AnkiService;
|
||||||
|
@ -70,6 +71,11 @@ class CreateProductionCommand extends Command
|
||||||
$allNotes = $this->ankiService->getNotes($allIds);
|
$allNotes = $this->ankiService->getNotes($allIds);
|
||||||
printf(" OK (%d)\n", count($allNotes));
|
printf(" OK (%d)\n", count($allNotes));
|
||||||
|
|
||||||
|
printf('Getting all SentenceCards...');
|
||||||
|
$allListeningIds = $this->ankiService->getAllSentenceListeningNoteIds();
|
||||||
|
$allListeningNotes = $this->ankiService->getNotes($allListeningIds);
|
||||||
|
printf(" OK (%d)\n", count($allListeningNotes));
|
||||||
|
|
||||||
printf('Indexing all terms...');
|
printf('Indexing all terms...');
|
||||||
$knownTerms = [];
|
$knownTerms = [];
|
||||||
$knownKanji = [];
|
$knownKanji = [];
|
||||||
|
@ -152,6 +158,16 @@ class CreateProductionCommand extends Command
|
||||||
|
|
||||||
arsort($termCounts);
|
arsort($termCounts);
|
||||||
|
|
||||||
|
// Have into account the ones that have already been created.
|
||||||
|
// This will not only skip them but take into account the kanjis they
|
||||||
|
// have.
|
||||||
|
foreach ($allListeningNotes as $listeningNote) {
|
||||||
|
assert($listeningNote instanceof SentenceListeningNote);
|
||||||
|
|
||||||
|
$termKanji = self::getOnlyKanji($listeningNote->getTerm()->getKanji());
|
||||||
|
self::kanjiDiff($seenKanji, $termKanji);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($termCounts as $term => $count) {
|
foreach ($termCounts as $term => $count) {
|
||||||
$termKanji = self::getOnlyKanji($term);
|
$termKanji = self::getOnlyKanji($term);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
class SentenceListeningNote extends Note
|
||||||
|
{
|
||||||
|
const MODEL_NAME = 'Japanese sentences listening';
|
||||||
|
|
||||||
|
private ?array $mediaInfo = [];
|
||||||
|
private ?Term $term = null;
|
||||||
|
|
||||||
|
// -------------------------------------------------- Getters & setters ---
|
||||||
|
|
||||||
|
public function getTerm(): Term
|
||||||
|
{
|
||||||
|
return $this->term;
|
||||||
|
}
|
||||||
|
public function setTerm(Term $terms): static
|
||||||
|
{
|
||||||
|
$this->term = $terms;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------- Anki-related ---
|
||||||
|
|
||||||
|
public static function fromAnki(array $noteInfo): static
|
||||||
|
{
|
||||||
|
$note = parent::fromAnki($noteInfo);
|
||||||
|
|
||||||
|
if ($note->getModel() !== self::MODEL_NAME) {
|
||||||
|
throw new \Exception('Trying to parse wrong model');
|
||||||
|
}
|
||||||
|
|
||||||
|
$note->mediaInfo = self::parseMediaInfo($note->fields['Notes']);
|
||||||
|
|
||||||
|
// Set VocabKanji field
|
||||||
|
$note->term = Term::fromNoteFields($note->fields)[0] ?? null;
|
||||||
|
if ($note->term === null) {
|
||||||
|
throw new \Exception("Couldn't get term for Listening card");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $note;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toAnki(): array
|
||||||
|
{
|
||||||
|
return $this->fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------- Derived methods ---
|
||||||
|
|
||||||
|
public function isSentKanjiHighlighted(): bool
|
||||||
|
{
|
||||||
|
return str_contains(
|
||||||
|
$this->fields['SentKanji'],
|
||||||
|
self::HIGHLIGHT_ATTR_KANJI,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function parseMediaInfo(string $notes): ?array
|
||||||
|
{
|
||||||
|
$matches = null;
|
||||||
|
|
||||||
|
// Parse the notes fields. It can be in the form of
|
||||||
|
// series-name_S01 EP07 (11h22m33s44ms)
|
||||||
|
// or
|
||||||
|
// movie-name EP (11h22m33s44ms)
|
||||||
|
if (1 !== preg_match(
|
||||||
|
'/(?<name>[0-9A-Za-z\-_]+)(_S)?(?<season>\d*) EP(?<episode>\d*) \((?<time>.*)\)/n',
|
||||||
|
$notes,
|
||||||
|
$matches,
|
||||||
|
)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove number-indexed matches, cast numbers to integers
|
||||||
|
$matches = [
|
||||||
|
'name' => $matches['name'],
|
||||||
|
'time' => $matches['time'],
|
||||||
|
// NOTE: intval returns 0 if not a number, which is false-like
|
||||||
|
'season' => intval($matches['season']) ?: null,
|
||||||
|
'episode' => intval($matches['episode']) ?: null,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Parse time into a DateInterval and replace it in the matches array
|
||||||
|
$time = new \DateInterval('PT0S');
|
||||||
|
|
||||||
|
preg_match('/(\d+)ms/', $matches['time'], $milliseconds);
|
||||||
|
preg_match('/(\d+)s/', $matches['time'], $seconds);
|
||||||
|
preg_match('/(\d+)m/', $matches['time'], $minutes);
|
||||||
|
preg_match('/(\d+)h/', $matches['time'], $hours);
|
||||||
|
|
||||||
|
if ($milliseconds[1] ?? false) $time->f = $milliseconds[1] * 1000;
|
||||||
|
if ($seconds[1] ?? false) $time->s = $seconds[1];
|
||||||
|
if ($minutes[1] ?? false) $time->i = $minutes[1];
|
||||||
|
if ($hours[1] ?? false) $time->h = $hours[1];
|
||||||
|
|
||||||
|
$matches['time'] = $time;
|
||||||
|
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Service;
|
namespace App\Service;
|
||||||
|
|
||||||
use App\Entity\Note;
|
use App\Entity\Note;
|
||||||
|
use App\Entity\SentenceListeningNote;
|
||||||
use App\Entity\SentenceNote;
|
use App\Entity\SentenceNote;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
@ -31,6 +32,16 @@ class AnkiService
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAllSentenceListeningNoteIds(): array
|
||||||
|
{
|
||||||
|
$query = sprintf(
|
||||||
|
'"note:%s" -is:suspended',
|
||||||
|
SentenceListeningNote::MODEL_NAME,
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->request('findNotes', ['query' => $query]);
|
||||||
|
}
|
||||||
|
|
||||||
public function getAllSentenceNoteIds(): array
|
public function getAllSentenceNoteIds(): array
|
||||||
{
|
{
|
||||||
return $this->request(
|
return $this->request(
|
||||||
|
@ -76,6 +87,7 @@ class AnkiService
|
||||||
{
|
{
|
||||||
return match ($noteInfo['modelName']) {
|
return match ($noteInfo['modelName']) {
|
||||||
SentenceNote::MODEL_NAME => SentenceNote::fromAnki($noteInfo),
|
SentenceNote::MODEL_NAME => SentenceNote::fromAnki($noteInfo),
|
||||||
|
SentenceListeningNote::MODEL_NAME => SentenceListeningNote::fromAnki($noteInfo),
|
||||||
default => throw new \Exception(sprintf(
|
default => throw new \Exception(sprintf(
|
||||||
'Unrecognized Note "%s" of type "%s"',
|
'Unrecognized Note "%s" of type "%s"',
|
||||||
$noteInfo['noteId'],
|
$noteInfo['noteId'],
|
||||||
|
|
Loading…
Reference in New Issue