158 lines
4.5 KiB
PHP
158 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Entity\Note;
|
|
use App\Entity\SentenceListeningNote;
|
|
use App\Entity\SentenceNote;
|
|
use App\Utils\Japanese;
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
|
|
class AnkiService
|
|
{
|
|
function __construct(
|
|
private HttpClientInterface $client,
|
|
) {}
|
|
|
|
private function request(string $action, array $params): mixed
|
|
{
|
|
['error' => $error, 'result' => $result] = $this->client->request(
|
|
'POST',
|
|
'http://localhost:8765',
|
|
['json' => [
|
|
'action' => $action,
|
|
'version' => 6,
|
|
'params' => $params,
|
|
]]
|
|
)->toArray();
|
|
|
|
if ($error != null) {
|
|
throw new \Exception('AnkiConnect returned error: ' . $error);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/** The note's id is updated on success.
|
|
* @return bool True on success
|
|
*/
|
|
public function addNote(Note &$note, string $deckName): bool
|
|
{
|
|
$note->setId($this->request('addNote', ['note' => [
|
|
'deckName' => $deckName,
|
|
'modelName' => $note->getModel(),
|
|
'fields' => $note->getFields(),
|
|
'options' => ['allowDuplicate' => false],
|
|
'tags' => array_merge($note->getTags(), ['anker_made']),
|
|
]]));
|
|
|
|
return $note->getId() !== null;
|
|
}
|
|
|
|
public function findNotesIds(string $query): array
|
|
{
|
|
return $this->request('findNotes', ['query' => $query]);
|
|
}
|
|
|
|
public function getAllSentenceListeningNoteIds(): array
|
|
{
|
|
$query = sprintf(
|
|
'"note:%s"',
|
|
SentenceListeningNote::MODEL_NAME,
|
|
);
|
|
|
|
return $this->request('findNotes', ['query' => $query]);
|
|
}
|
|
|
|
public function getAllSentenceNoteIds(): array
|
|
{
|
|
return $this->request(
|
|
'findNotes',
|
|
['query' => '"note:Japanese sentences" -is:suspended'],
|
|
);
|
|
}
|
|
|
|
/** Give an array of IDs, the note Infos are returned. if info for a given
|
|
* doesn't exist, it is assigned to null instead of the default [].
|
|
*/
|
|
public function getNotesInfo(array $noteIds): array
|
|
{
|
|
$noteInfos = $this->request('notesInfo', ['notes' => $noteIds]);
|
|
|
|
foreach ($noteInfos as &$noteInfo) {
|
|
if ([] === $noteInfo) $noteInfo = null;
|
|
}
|
|
|
|
return $noteInfos;
|
|
}
|
|
|
|
/** Returns info form note given an ID, returns null if it doesn't exist */
|
|
public function getNoteInfo(int $noteId): ?array
|
|
{
|
|
return $this->getNotesInfo([$noteId])[0];
|
|
}
|
|
|
|
public function getNotes(array $nids): array
|
|
{
|
|
return array_map(self::parseNoteInfo(...), $this->getNotesInfo($nids));
|
|
}
|
|
|
|
public function getNote(int $nid): ?Note
|
|
{
|
|
$noteInfo = $this->getNoteInfo($nid)
|
|
?? throw new \Exception("Note $nid not found.");
|
|
|
|
return self::parseNoteInfo($noteInfo);
|
|
}
|
|
|
|
private static function parseNoteInfo(array $noteInfo): Note
|
|
{
|
|
return match ($noteInfo['modelName']) {
|
|
SentenceNote::MODEL_NAME => SentenceNote::fromAnki($noteInfo),
|
|
SentenceListeningNote::MODEL_NAME => SentenceListeningNote::fromAnki($noteInfo),
|
|
default => throw new \Exception(sprintf(
|
|
'Unrecognized Note "%s" of type "%s"',
|
|
$noteInfo['noteId'],
|
|
$noteInfo['modelName'],
|
|
))
|
|
};
|
|
}
|
|
|
|
public function getLatestNote(): ?Note
|
|
{
|
|
// NoteIDs are just timestamps in milliseconds, so the latest is just
|
|
// the biggest numerically
|
|
$latestId = max($this->getAllSentenceNoteIds());
|
|
return $this->getNote($latestId);
|
|
}
|
|
|
|
public function updateNote(Note $note)
|
|
{
|
|
$this->request('guiBrowse', ['query' => 'nid:1']);
|
|
$this->request('updateNoteFields', ['note' => $note->toAnki()]);
|
|
$this->request('guiBrowse', ['query' => 'nid:' . $note->getId()]);
|
|
}
|
|
|
|
/** @return array<string, int> */
|
|
public function getKnownSlnKanjiCounts(): array
|
|
{
|
|
$allListeningIds = $this->getAllSentenceListeningNoteIds();
|
|
$ret = [];
|
|
|
|
foreach ($this->getNotes($allListeningIds) as $slNote) {
|
|
assert($slNote instanceof SentenceListeningNote);
|
|
|
|
$termKanji = Japanese::getOnlyKanji($slNote->getTerm()->getKanji());
|
|
$len = mb_strlen($termKanji);
|
|
for ($i = 0; $i < $len; $i++) {
|
|
$kanji = mb_substr($termKanji, $i, 1);
|
|
|
|
$ret[$kanji] ??= 0;
|
|
$ret[$kanji]++;
|
|
}
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
}
|