From 738f022212c304f1b0eb2b51cd742f4570e5e062 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:45:29 +0100 Subject: [PATCH] Add a quick document read function in the storage class --- novelwriter/core/storage.py | 23 +++++++++++++++++++++-- tests/test_core/test_core_storage.py | 17 +++++++++++++---- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/novelwriter/core/storage.py b/novelwriter/core/storage.py index 2740d19d..59ea24b3 100644 --- a/novelwriter/core/storage.py +++ b/novelwriter/core/storage.py @@ -27,18 +27,18 @@ import json import logging from enum import Enum +from pathlib import Path from time import time from typing import TYPE_CHECKING -from pathlib import Path from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile from novelwriter import CONFIG -from novelwriter.error import logException from novelwriter.common import isHandle, minmax from novelwriter.constants import nwFiles from novelwriter.core.document import NWDocument from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter from novelwriter.core.spellcheck import UserDictionary +from novelwriter.error import logException if TYPE_CHECKING: # pragma: no cover from novelwriter.core.project import NWProject @@ -287,6 +287,25 @@ class NWStorage: return self._runtimePath / "meta" / fileName return None + def getDocumentText(self, tHandle: str) -> str: + """Return the text of a document in a fast and efficient way.""" + if ( + isinstance(self._runtimePath, Path) + and (path := self._runtimePath / "content" / f"{tHandle}.nwd").is_file() + ): + try: + with open(path, mode="r", encoding="utf-8") as inFile: + line = "" + for _ in range(10): + if not (line := inFile.readline()).startswith(r"%%~"): + break + return line + inFile.read() + except Exception: + logger.error("Cannot read document with handle '%s'", tHandle) + logException() + return "" + return "" + def scanContent(self) -> list[str]: """Scan the content folder and return the handle of all files found in it. Files that do not match the pattern are ignored. diff --git a/tests/test_core/test_core_storage.py b/tests/test_core/test_core_storage.py index 213cf9b3..6ea0937d 100644 --- a/tests/test_core/test_core_storage.py +++ b/tests/test_core/test_core_storage.py @@ -26,15 +26,15 @@ import pytest from pathlib import Path from zipfile import ZipFile -from tools import C, buildTestProject from mocked import causeOSError +from tools import C, buildTestProject from novelwriter import CONFIG from novelwriter.constants import nwFiles -from novelwriter.core.project import NWProject -from novelwriter.core.storage import NWStorage, NWStorageOpen, NWStorageCreate, _LegacyStorage from novelwriter.core.document import NWDocument +from novelwriter.core.project import NWProject from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter +from novelwriter.core.storage import NWStorage, NWStorageOpen, NWStorageCreate, _LegacyStorage class MockProject: @@ -85,7 +85,7 @@ def testCoreStorage_CreateNewProject(mockGUI, fncPath): @pytest.mark.core -def testCoreStorage_InitProjectStorage(mockGUI, fncPath, mockRnd): +def testCoreStorage_InitProjectStorage(monkeypatch, mockGUI, fncPath, mockRnd): """Test initialising a project in a folder.""" project = NWProject() @@ -157,6 +157,15 @@ def testCoreStorage_InitProjectStorage(mockGUI, fncPath, mockRnd): assert isinstance(storage.getDocument(C.hSceneDoc), NWDocument) assert repr(storage.getDocument(C.hSceneDoc)) == f"" + # We can directly access the content of a document + assert storage.getDocumentText(C.hSceneDoc) == "### New Scene\n\n" + + # Check read text fallback + assert storage.getDocumentText(C.hInvalid) == "" + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert storage.getDocumentText(C.hSceneDoc) == "" + project.closeProject() # END Test testCoreStorage_InitProjectStorage