From eadd8a01ea94e629cdb5a9fbf21e04464f258075 Mon Sep 17 00:00:00 2001 From: Dendy Faist Date: Sun, 9 Feb 2025 23:15:57 +0900 Subject: [PATCH] feat: Separate SentenceNote into its own extension of Note --- src/Controller/AnkiController.php | 32 +++++- src/Entity/Note.php | 166 ++++------------------------ src/Entity/SentenceNote.php | 177 ++++++++++++++++++++++++++++++ src/Service/AnkiService.php | 24 +++- 4 files changed, 249 insertions(+), 150 deletions(-) create mode 100644 src/Entity/SentenceNote.php diff --git a/src/Controller/AnkiController.php b/src/Controller/AnkiController.php index 951e3f7..6b2f521 100644 --- a/src/Controller/AnkiController.php +++ b/src/Controller/AnkiController.php @@ -3,6 +3,8 @@ namespace App\Controller; use App\Entity\Note; +use App\Entity\SentenceNote; +use App\Entity\Term; use App\Service\AnkiService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -23,11 +25,35 @@ class AnkiController extends AbstractController #[Route('/', name: 'main')] public function index(): Response { - $allIds = $this->ankiService->getAllNoteIds(); - + $allIds = $this->ankiService->getAllSentenceNoteIds(); $allNotes = $this->ankiService->getNotes($allIds); - dd($allNotes); + $kanjiNotes = []; + foreach ($allNotes as $note) { + if (!$note instanceof SentenceNote) throw new \Exception(sprintf( + 'Expected SentenceNote, got %s', + $note::class, + )); + + foreach ($note->getTerms() as $term) { + assert($term instanceof Term); + + if (key_exists($term->getKanji(), $kanjiNotes)) continue; + + $newNote = new Note(); + + echo $note->getFields()['SentKanji']; + echo '
'; + echo var_dump($note->getHighlightedKanji()); + echo '

'; + //echo $term->getKanji(); + //echo ' | '; + + $kanjiNotes[$term->getKanji()] = $newNote; + } + } + + die(); } #[Route('/nonconforming', name: 'nonconforming')] diff --git a/src/Entity/Note.php b/src/Entity/Note.php index d16c7d4..4e2df4b 100644 --- a/src/Entity/Note.php +++ b/src/Entity/Note.php @@ -2,58 +2,47 @@ namespace App\Entity; -//use App\Repository\NoteRepository; -//use Doctrine\ORM\Mapping as ORM; - -//#[ORM\Entity(repositoryClass: NoteRepository::class)] class Note { - //#[ORM\Id] - //#[ORM\GeneratedValue] - //#[ORM\Column] - - private int $id; - private int $mod; - private array $terms = []; - private string $profile; - private array $tags = []; - private string $model; - // Maybe these doesn't make sense to keep but leaving it here just in - // case for handiness' sake - private array $fields = []; - private ?array $mediaInfo = null; - private array $cardIds; - const HIGHLIGHT_PATTERN = '/]*)>(.*?)<\/span>/i'; const HIGHLIGHT_ATTR_KANJI = 'style="color: rgb(255, 78, 8);"'; + private readonly int $id; + private readonly int $mod; + private readonly string $model; + private string $profile; + private array $cardIds = []; + protected array $fields = []; + private array $tags = []; + + + // -------------------------------------------------- Getters & setters --- + public function getId(): int { return $this->id; } - public function getTerms(): array + public function getModel(): string { - return $this->terms; + return $this->model; } + public function getFields(): array { return $this->fields; } - - public function hasTerm(string $kanji): bool + public function setFields(array $fields): static { - foreach ($this->terms as $term) { - assert($term instanceof Term); - - if ($term->kanji == $kanji) return true; - } - - return false; + $this->fields = $fields; + return $this; } - public static function fromAnki(array $noteInfo): self + + // ------------------------------------------------------- Anki-related --- + + public static function fromAnki(array $noteInfo): static { - $note = new self(); + $note = new static(); [ 'noteId' => $note->id, @@ -72,127 +61,18 @@ class Note // the order would be advisable. $note->fields = array_map(fn($x) => $x['value'], $noteInfo['fields']); - $note->mediaInfo = $note->parseMediaInfo($note->fields['Notes']); - - // Set VocabKanji field - $note->terms = Term::fromNoteFields($note->fields); - - // If unable to, create them from the highlighted parts in the sentence - if (empty($note->terms)) { - foreach ($note->getHighlightedKanji() as $highlighedKanji) { - $term = new Term(); - $term->kanji = $highlighedKanji; - $term->definitionEn = null; - $term->definitionJp = null; - $note->terms[] = $term; - } - } - - // Set to null whatever is null - $readings = array_map( - fn($x) => in_array($x, ['_', '_', '']) ? null : $x, - explode('|', $note->fields['VocabFurigana']), - ); - - // Set readings from furigana field - foreach ($note->terms as $key => &$term) { - if (null === $term->getReading()) { - if (null !== ($readings[$key] ?? null)) { - $term->kanji .= '[' . $readings[$key] . ']'; - } - } - } - return $note; } - /** Return an array of strings with the highlighted kanji in the SentKanji */ - public function getHighlightedKanji(): array - { - $ret = []; - $matches = []; - - // 1. Get all spans in the text - preg_match_all( - self::HIGHLIGHT_PATTERN, - $this->fields['SentKanji'], - $matches, - PREG_SET_ORDER, - ); - - // 2. Check the ones that match with the kanji color - foreach ($matches as $match) { - if ($match[1] === self::HIGHLIGHT_ATTR_KANJI) { - $ret[] = mb_trim($match[2]); - } - } - - return $ret; - } - public function toAnki(): array { return [ 'id' => $this->id, - 'fields' => [ - 'VocabKanji' => join('|', array_map( - fn(Term $x) => $x->getKanji(), - $this->terms, - )), - 'VocabFurigana' => join('|', array_map( - fn(Term $x) => $x->getReading() ?? '_', - $this->terms, - )), - 'VocabDef' => join("
\n", array_map( - fn(Term $x) => $x->toAnkiVocabDef(), - $this->terms, - )), - ], ]; } - public 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( - '/(?[a-z\-_]+)(_S)?(?\d*) EP(?\d*) \((?