Standardise the save document call for all document classes

This commit is contained in:
Veronica Berglyd Olsen
2024-10-15 21:25:36 +02:00
parent 759a8e84f0
commit fc27078954
11 changed files with 131 additions and 123 deletions
+4 -13
View File
@@ -178,10 +178,7 @@ class NWBuildDocument:
self._cache = makeObj self._cache = makeObj
try: try:
if isFlat: makeObj.saveDocument(path)
makeObj.saveFlatXML(path)
else:
makeObj.saveOpenDocText(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -213,10 +210,7 @@ class NWBuildDocument:
if isinstance(path, Path): if isinstance(path, Path):
try: try:
if asJson: makeObj.saveDocument(path, asJson=asJson)
makeObj.saveHtmlJson(path)
else:
makeObj.saveHtml5(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -246,7 +240,7 @@ class NWBuildDocument:
self._cache = makeObj self._cache = makeObj
try: try:
makeObj.saveMarkdown(path) makeObj.saveDocument(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
@@ -276,10 +270,7 @@ class NWBuildDocument:
if isinstance(path, Path): if isinstance(path, Path):
try: try:
if asJson: makeObj.saveRawDocument(path, asJson=asJson)
makeObj.saveRawMarkdownJSON(path)
else:
makeObj.saveRawMarkdown(path)
except Exception as exc: except Exception as exc:
logException() logException()
self._error = formatException(exc) self._error = formatException(exc)
+20 -20
View File
@@ -290,8 +290,26 @@ class ToHtml(Tokenizer):
return return
def saveHtml5(self, path: str | Path) -> None: def saveDocument(self, path: str | Path, asJson: bool = False) -> None:
"""Save the data to an HTML file.""" """Save the data to an HTML file."""
if asJson:
ts = time()
data = {
"meta": {
"projectName": self._project.data.name,
"novelAuthor": self._project.data.author,
"buildTime": int(ts),
"buildTimeStr": formatTimeStamp(ts),
},
"text": {
"css": self.getStyleSheet(),
"html": [t.replace("\t", "	").rstrip().split("\n") for t in self.fullHTML],
}
}
with open(path, mode="w", encoding="utf-8") as fObj:
json.dump(data, fObj, indent=2)
else:
with open(path, mode="w", encoding="utf-8") as fObj: with open(path, mode="w", encoding="utf-8") as fObj:
fObj.write(( fObj.write((
"<!DOCTYPE html>\n" "<!DOCTYPE html>\n"
@@ -314,27 +332,9 @@ class ToHtml(Tokenizer):
style="\n".join(self.getStyleSheet()), style="\n".join(self.getStyleSheet()),
body=("".join(self._fullHTML)).replace("\t", "&#09;").rstrip(), body=("".join(self._fullHTML)).replace("\t", "&#09;").rstrip(),
)) ))
logger.info("Wrote file: %s", path)
return
def saveHtmlJson(self, path: str | Path) -> None:
"""Save the data to a JSON file."""
timeStamp = time()
data = {
"meta": {
"projectName": self._project.data.name,
"novelAuthor": self._project.data.author,
"buildTime": int(timeStamp),
"buildTimeStr": formatTimeStamp(timeStamp),
},
"text": {
"css": self.getStyleSheet(),
"html": [t.replace("\t", "&#09;").rstrip().split("\n") for t in self.fullHTML],
}
}
with open(path, mode="w", encoding="utf-8") as fObj:
json.dump(data, fObj, indent=2)
logger.info("Wrote file: %s", path) logger.info("Wrote file: %s", path)
return return
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None: def replaceTabs(self, nSpaces: int = 8, spaceChar: str = "&nbsp;") -> None:
+17 -11
View File
@@ -486,6 +486,10 @@ class Tokenizer(ABC):
def doConvert(self) -> None: def doConvert(self) -> None:
raise NotImplementedError raise NotImplementedError
@abstractmethod
def saveDocument(self, path: str | Path) -> None:
raise NotImplementedError
def addRootHeading(self, tHandle: str) -> None: def addRootHeading(self, tHandle: str) -> None:
"""Add a heading at the start of a new root folder.""" """Add a heading at the start of a new root folder."""
self._text = "" self._text = ""
@@ -1087,22 +1091,16 @@ class Tokenizer(ABC):
return return
def saveRawMarkdown(self, path: str | Path) -> None: def saveRawDocument(self, path: str | Path, asJson: bool = False) -> None:
"""Save the raw text to a plain text file.""" """Save the raw text to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: if asJson:
for nwdPage in self._markdown: ts = time()
outFile.write(nwdPage)
return
def saveRawMarkdownJSON(self, path: str | Path) -> None:
"""Save the raw text to a JSON file."""
timeStamp = time()
data = { data = {
"meta": { "meta": {
"projectName": self._project.data.name, "projectName": self._project.data.name,
"novelAuthor": self._project.data.author, "novelAuthor": self._project.data.author,
"buildTime": int(timeStamp), "buildTime": int(ts),
"buildTimeStr": formatTimeStamp(timeStamp), "buildTimeStr": formatTimeStamp(ts),
}, },
"text": { "text": {
"nwd": [page.rstrip("\n").split("\n") for page in self._markdown], "nwd": [page.rstrip("\n").split("\n") for page in self._markdown],
@@ -1110,6 +1108,14 @@ class Tokenizer(ABC):
} }
with open(path, mode="w", encoding="utf-8") as fObj: with open(path, mode="w", encoding="utf-8") as fObj:
json.dump(data, fObj, indent=2) json.dump(data, fObj, indent=2)
else:
with open(path, mode="w", encoding="utf-8") as outFile:
for nwdPage in self._markdown:
outFile.write(nwdPage)
logger.info("Wrote file: %s", path)
return return
## ##
+1 -1
View File
@@ -199,7 +199,7 @@ class ToMarkdown(Tokenizer):
return return
def saveMarkdown(self, path: str | Path) -> None: def saveDocument(self, path: str | Path) -> None:
"""Save the data to a plain text file.""" """Save the data to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: with open(path, mode="w", encoding="utf-8") as outFile:
outFile.write("".join(self._fullMD)) outFile.write("".join(self._fullMD))
+4 -6
View File
@@ -543,17 +543,15 @@ class ToOdt(Tokenizer):
self._xText.insert(0, xFields) self._xText.insert(0, xFields)
return return
def saveFlatXML(self, path: str | Path) -> None: def saveDocument(self, path: str | Path) -> None:
"""Save the data to an .fodt file.""" """Save the data to an .fodt or .odt file."""
if self._isFlat:
with open(path, mode="wb") as fObj: with open(path, mode="wb") as fObj:
xml = ET.ElementTree(self._dFlat) xml = ET.ElementTree(self._dFlat)
xmlIndent(xml) xmlIndent(xml)
xml.write(fObj, encoding="utf-8", xml_declaration=True) xml.write(fObj, encoding="utf-8", xml_declaration=True)
logger.info("Wrote file: %s", path)
return
def saveOpenDocText(self, path: str | Path) -> None: else:
"""Save the data to an .odt file."""
mMani = _mkTag("manifest", "manifest") mMani = _mkTag("manifest", "manifest")
mVers = _mkTag("manifest", "version") mVers = _mkTag("manifest", "version")
mPath = _mkTag("manifest", "full-path") mPath = _mkTag("manifest", "full-path")
+6
View File
@@ -25,6 +25,8 @@ from __future__ import annotations
import logging import logging
from pathlib import Path
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat, QColor, QFont, QFontMetricsF, QTextBlockFormat, QTextCharFormat,
QTextCursor, QTextDocument QTextCursor, QTextDocument
@@ -273,6 +275,10 @@ class ToQTextDocument(Tokenizer):
return return
def saveDocument(self, path: str | Path) -> None:
"""Not implemented."""
return
def appendFootnotes(self) -> None: def appendFootnotes(self) -> None:
"""Append the footnotes in the buffer.""" """Append the footnotes in the buffer."""
if self._usedNotes: if self._usedNotes:
+2 -2
View File
@@ -644,12 +644,12 @@ def testFmtToHtml_Save(mockGUI, fncPath):
) )
saveFile = fncPath / "outFile.htm" saveFile = fncPath / "outFile.htm"
html.saveHtml5(saveFile) html.saveDocument(saveFile, asJson=False)
assert saveFile.read_text(encoding="utf-8") == htmlDoc assert saveFile.read_text(encoding="utf-8") == htmlDoc
# JSON + HTML # JSON + HTML
saveFile = fncPath / "outFile.json" saveFile = fncPath / "outFile.json"
html.saveHtmlJson(saveFile) html.saveDocument(saveFile, asJson=True)
data = json.loads(saveFile.read_text(encoding="utf-8")) data = json.loads(saveFile.read_text(encoding="utf-8"))
assert data["meta"]["projectName"] == "" assert data["meta"]["projectName"] == ""
assert data["meta"]["novelAuthor"] == "" assert data["meta"]["novelAuthor"] == ""
+9 -3
View File
@@ -39,6 +39,9 @@ class BareTokenizer(Tokenizer):
def doConvert(self): def doConvert(self):
super().doConvert() # type: ignore (deliberate check) super().doConvert() # type: ignore (deliberate check)
def saveDocument(self, path) -> None:
super().saveDocument(path) # type: ignore (deliberate check)
@pytest.mark.core @pytest.mark.core
def testFmtToken_Setters(mockGUI): def testFmtToken_Setters(mockGUI):
@@ -219,12 +222,12 @@ def testFmtToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
# Save File # Save File
savePath = fncPath / "dump.nwd" savePath = fncPath / "dump.nwd"
tokens.saveRawMarkdown(savePath) tokens.saveRawDocument(savePath, asJson=False)
assert readFile(savePath) == ( assert readFile(savePath) == (
"#! Notes: Plot\n\n" "#! Notes: Plot\n\n"
"#! Notes: Plot\n\n" "#! Notes: Plot\n\n"
) )
tokens.saveRawMarkdownJSON(savePath) tokens.saveRawDocument(savePath, asJson=True)
assert json.loads(readFile(savePath))["text"] == { assert json.loads(readFile(savePath))["text"] == {
"nwd": [ "nwd": [
["#! Notes: Plot"], ["#! Notes: Plot"],
@@ -232,10 +235,13 @@ def testFmtToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
] ]
} }
# Check abstract method # Check abstract methods
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
tokens.doConvert() tokens.doConvert()
with pytest.raises(NotImplementedError):
tokens.saveDocument(fncPath)
@pytest.mark.core @pytest.mark.core
def testFmtToken_StripEscape(): def testFmtToken_StripEscape():
+1 -1
View File
@@ -293,7 +293,7 @@ def testFmtToMarkdown_Save(mockGUI, fncPath):
# ========== # ==========
saveFile = fncPath / "outFile.md" saveFile = fncPath / "outFile.md"
toMD.saveMarkdown(saveFile) toMD.saveDocument(saveFile)
assert saveFile.read_text(encoding="utf-8") == "".join(resText) assert saveFile.read_text(encoding="utf-8") == "".join(resText)
+2 -2
View File
@@ -802,7 +802,7 @@ def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
testFile = tstPaths.outDir / "coreToOdt_SaveFlat_document.fodt" testFile = tstPaths.outDir / "coreToOdt_SaveFlat_document.fodt"
compFile = tstPaths.refDir / "coreToOdt_SaveFlat_document.fodt" compFile = tstPaths.refDir / "coreToOdt_SaveFlat_document.fodt"
odt.saveFlatXML(flatFile) odt.saveDocument(flatFile)
assert flatFile.exists() assert flatFile.exists()
copyfile(flatFile, testFile) copyfile(flatFile, testFile)
@@ -837,7 +837,7 @@ def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths):
fullFile = fncPath / "document.odt" fullFile = fncPath / "document.odt"
odt.saveOpenDocText(fullFile) odt.saveDocument(fullFile)
assert fullFile.exists() assert fullFile.exists()
assert zipfile.is_zipfile(fullFile) assert zipfile.is_zipfile(fullFile)
+1
View File
@@ -61,6 +61,7 @@ def testFmtToQTextDocument_ConvertHeaders(mockGUI):
project = NWProject() project = NWProject()
qdoc = ToQTextDocument(project) qdoc = ToQTextDocument(project)
qdoc.initDocument(CONFIG.textFont, THEME) qdoc.initDocument(CONFIG.textFont, THEME)
qdoc.saveDocument("") # Doesn't do anything for this format
qdoc._isNovel = True qdoc._isNovel = True
qdoc._isFirst = True qdoc._isFirst = True