Complete annotations in project class and update tests
This commit is contained in:
@@ -77,7 +77,7 @@ class OptionState:
|
|||||||
the Config instead.
|
the Config instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project: NWProject):
|
def __init__(self, project: NWProject) -> None:
|
||||||
self._project = project
|
self._project = project
|
||||||
self._state = {}
|
self._state = {}
|
||||||
return
|
return
|
||||||
@@ -87,8 +87,7 @@ class OptionState:
|
|||||||
##
|
##
|
||||||
|
|
||||||
def loadSettings(self) -> bool:
|
def loadSettings(self) -> bool:
|
||||||
"""Load the options dictionary from the project settings file.
|
"""Load the options dictionary from the project."""
|
||||||
"""
|
|
||||||
stateFile = self._project.storage.getMetaFile(nwFiles.OPTS_FILE)
|
stateFile = self._project.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||||
if not isinstance(stateFile, Path):
|
if not isinstance(stateFile, Path):
|
||||||
return False
|
return False
|
||||||
@@ -116,7 +115,7 @@ class OptionState:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def saveSettings(self) -> bool:
|
def saveSettings(self) -> bool:
|
||||||
"""Save the options dictionary to the project settings file."""
|
"""Save the options dictionary to the project."""
|
||||||
stateFile = self._project.storage.getMetaFile(nwFiles.OPTS_FILE)
|
stateFile = self._project.storage.getMetaFile(nwFiles.OPTS_FILE)
|
||||||
if not isinstance(stateFile, Path):
|
if not isinstance(stateFile, Path):
|
||||||
return False
|
return False
|
||||||
|
|||||||
+68
-68
@@ -28,6 +28,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
|
from typing import TYPE_CHECKING, Iterator
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
@@ -48,6 +49,11 @@ from novelwriter.common import (
|
|||||||
checkStringNone, formatTimeStamp, hexToInt, makeFileNameSafe, minmax
|
checkStringNone, formatTimeStamp, hexToInt, makeFileNameSafe, minmax
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
from novelwriter.guimain import GuiMain
|
||||||
|
from novelwriter.core.item import NWItem
|
||||||
|
from novelwriter.core.status import NWStatus
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -55,7 +61,7 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
projectStatusChanged = pyqtSignal(bool)
|
projectStatusChanged = pyqtSignal(bool)
|
||||||
|
|
||||||
def __init__(self, mainGui):
|
def __init__(self, mainGui: GuiMain) -> None:
|
||||||
super().__init__(parent=mainGui)
|
super().__init__(parent=mainGui)
|
||||||
|
|
||||||
# Internal
|
# Internal
|
||||||
@@ -69,19 +75,14 @@ class NWProject(QObject):
|
|||||||
self._index = NWIndex(self) # The project index
|
self._index = NWIndex(self) # The project index
|
||||||
self._session = NWSessionLog(self) # The session record
|
self._session = NWSessionLog(self) # The session record
|
||||||
|
|
||||||
# Data Cache
|
|
||||||
self._langData = {} # Localisation data
|
|
||||||
|
|
||||||
# Project Status
|
# Project Status
|
||||||
|
self._langData = {} # Localisation data
|
||||||
self._projChanged = False # The project has unsaved changes
|
self._projChanged = False # The project has unsaved changes
|
||||||
self._lockedBy = None # Data on which computer has the project open
|
self._lockedBy = None # Data on which computer has the project open
|
||||||
|
|
||||||
# Internal Mapping
|
# Internal Mapping
|
||||||
self.tr = partial(QCoreApplication.translate, "NWProject")
|
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||||
|
|
||||||
# Set Defaults
|
|
||||||
self.clearProject()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -89,23 +90,23 @@ class NWProject(QObject):
|
|||||||
##
|
##
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def options(self):
|
def options(self) -> OptionState:
|
||||||
return self._options
|
return self._options
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def storage(self):
|
def storage(self) -> NWStorage:
|
||||||
return self._storage
|
return self._storage
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self) -> NWProjectData:
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tree(self):
|
def tree(self) -> NWTree:
|
||||||
return self._tree
|
return self._tree
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def index(self):
|
def index(self) -> NWIndex:
|
||||||
return self._index
|
return self._index
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -113,11 +114,11 @@ class NWProject(QObject):
|
|||||||
return self._session
|
return self._session
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def projOpened(self):
|
def projOpened(self) -> float:
|
||||||
return self._session.start
|
return self._session.start
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def projChanged(self):
|
def projChanged(self) -> bool:
|
||||||
return self._projChanged
|
return self._projChanged
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -139,7 +140,7 @@ class NWProject(QObject):
|
|||||||
"""Add a new file with a given label and parent item."""
|
"""Add a new file with a given label and parent item."""
|
||||||
return self._tree.create(label, parent, nwItemType.FILE)
|
return self._tree.create(label, parent, nwItemType.FILE)
|
||||||
|
|
||||||
def writeNewFile(self, tHandle, hLevel, isDocument, addText=""):
|
def writeNewFile(self, tHandle: str, hLevel: int, isDocument: bool, addText: str = "") -> bool:
|
||||||
"""Write content to a new document after it is created. This
|
"""Write content to a new document after it is created. This
|
||||||
will not run if the file exists and is not empty.
|
will not run if the file exists and is not empty.
|
||||||
"""
|
"""
|
||||||
@@ -165,7 +166,7 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def removeItem(self, tHandle):
|
def removeItem(self, tHandle: str) -> bool:
|
||||||
"""Remove an item from the project. This will delete both the
|
"""Remove an item from the project. This will delete both the
|
||||||
project entry and a document file if it exists.
|
project entry and a document file if it exists.
|
||||||
"""
|
"""
|
||||||
@@ -182,7 +183,7 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def trashFolder(self):
|
def trashFolder(self) -> str:
|
||||||
"""Add the special trash root folder to the project."""
|
"""Add the special trash root folder to the project."""
|
||||||
trashHandle = self._tree.trashRoot()
|
trashHandle = self._tree.trashRoot()
|
||||||
if trashHandle is None:
|
if trashHandle is None:
|
||||||
@@ -194,23 +195,28 @@ class NWProject(QObject):
|
|||||||
# Project Methods
|
# Project Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def clearProject(self):
|
def clearProject(self) -> None:
|
||||||
"""Clear the data for the current project, and set them to
|
"""Clear the data for the current project, and set them to
|
||||||
default values.
|
default values.
|
||||||
"""
|
|
||||||
# Project Status
|
|
||||||
self._projChanged = False
|
|
||||||
|
|
||||||
# Project Tree
|
Note: Don't clear the lockedBy data here as it is needed after
|
||||||
|
this function is called.
|
||||||
|
"""
|
||||||
|
# Core Elements
|
||||||
|
self._options = OptionState(self)
|
||||||
self._storage.clear()
|
self._storage.clear()
|
||||||
|
self._data = NWProjectData(self)
|
||||||
self._tree.clear()
|
self._tree.clear()
|
||||||
self._index.clearIndex()
|
self._index.clearIndex()
|
||||||
self._data = NWProjectData(self)
|
|
||||||
self._session = NWSessionLog(self)
|
self._session = NWSessionLog(self)
|
||||||
|
|
||||||
|
# Project Status
|
||||||
|
self._langData = {}
|
||||||
|
self._projChanged = False
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def openProject(self, projPath, overrideLock=False):
|
def openProject(self, projPath: str | Path, overrideLock: bool = False) -> bool:
|
||||||
"""Open the project file provided. If it doesn't exist, assume
|
"""Open the project file provided. If it doesn't exist, assume
|
||||||
it is a folder and look for the file within it. If successful,
|
it is a folder and look for the file within it. If successful,
|
||||||
parse the XML of the file and populate the project variables and
|
parse the XML of the file and populate the project variables and
|
||||||
@@ -342,7 +348,7 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def saveProject(self, autoSave=False):
|
def saveProject(self, autoSave: bool = False) -> bool:
|
||||||
"""Save the project main XML file. The saving command itself
|
"""Save the project main XML file. The saving command itself
|
||||||
uses a temporary filename, and the file is replaced afterwards
|
uses a temporary filename, and the file is replaced afterwards
|
||||||
to make sure if the save fails, we're not left with a truncated
|
to make sure if the save fails, we're not left with a truncated
|
||||||
@@ -395,9 +401,8 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def closeProject(self, idleTime=0.0):
|
def closeProject(self, idleTime: float = 0.0) -> None:
|
||||||
"""Close the current project and clear all meta data.
|
"""Close the current project and clear all meta data."""
|
||||||
"""
|
|
||||||
logger.info("Closing project")
|
logger.info("Closing project")
|
||||||
self._options.saveSettings()
|
self._options.saveSettings()
|
||||||
self._tree.writeToCFile()
|
self._tree.writeToCFile()
|
||||||
@@ -406,11 +411,10 @@ class NWProject(QObject):
|
|||||||
self._storage.closeSession()
|
self._storage.closeSession()
|
||||||
self.clearProject()
|
self.clearProject()
|
||||||
self._lockedBy = None
|
self._lockedBy = None
|
||||||
return True
|
return
|
||||||
|
|
||||||
def backupProject(self, doNotify):
|
def backupProject(self, doNotify: bool) -> bool:
|
||||||
"""Create a zip file of the entire project.
|
"""Create a zip file of the entire project."""
|
||||||
"""
|
|
||||||
if not self._storage.isOpen():
|
if not self._storage.isOpen():
|
||||||
logger.error("No project open")
|
logger.error("No project open")
|
||||||
return False
|
return False
|
||||||
@@ -467,9 +471,8 @@ class NWProject(QObject):
|
|||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|
||||||
def setDefaultStatusImport(self):
|
def setDefaultStatusImport(self) -> None:
|
||||||
"""Set the default status and importance values.
|
"""Set the default status and importance values."""
|
||||||
"""
|
|
||||||
self._data.itemStatus.write(None, self.tr("New"), (100, 100, 100))
|
self._data.itemStatus.write(None, self.tr("New"), (100, 100, 100))
|
||||||
self._data.itemStatus.write(None, self.tr("Note"), (200, 50, 0))
|
self._data.itemStatus.write(None, self.tr("Note"), (200, 50, 0))
|
||||||
self._data.itemStatus.write(None, self.tr("Draft"), (200, 150, 0))
|
self._data.itemStatus.write(None, self.tr("Draft"), (200, 150, 0))
|
||||||
@@ -480,43 +483,40 @@ class NWProject(QObject):
|
|||||||
self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0))
|
self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0))
|
||||||
return
|
return
|
||||||
|
|
||||||
def setProjectLang(self, theLang):
|
def setProjectLang(self, language: str | None) -> None:
|
||||||
"""Set the project-specific language.
|
"""Set the project-specific language."""
|
||||||
"""
|
language = checkStringNone(language, None)
|
||||||
theLang = checkStringNone(theLang, None)
|
if self._data.language != language:
|
||||||
if self._data.language != theLang:
|
self._data.setLanguage(language)
|
||||||
self._data.setLanguage(theLang)
|
|
||||||
self._loadProjectLocalisation()
|
self._loadProjectLocalisation()
|
||||||
self.setProjectChanged(True)
|
self.setProjectChanged(True)
|
||||||
return True
|
return
|
||||||
|
|
||||||
def setTreeOrder(self, newOrder):
|
def setTreeOrder(self, order: list[str]) -> None:
|
||||||
"""A list representing the linear/flattened order of project
|
"""A list representing the linear/flattened order of project
|
||||||
items in the GUI project tree. The user can rearrange the order
|
items in the GUI project tree. The user can rearrange the order
|
||||||
by drag-and-drop. Forwarded to the NWTree class.
|
by drag-and-drop. Forwarded to the NWTree class.
|
||||||
"""
|
"""
|
||||||
if len(self._tree) != len(newOrder):
|
if len(self._tree) != len(order):
|
||||||
logger.warning("Sizes of new and old tree order do not match")
|
logger.warning("Sizes of new and old tree order do not match")
|
||||||
self._tree.setOrder(newOrder)
|
self._tree.setOrder(order)
|
||||||
self.setProjectChanged(True)
|
self.setProjectChanged(True)
|
||||||
return True
|
return
|
||||||
|
|
||||||
def setStatusColours(self, newCols, delCols):
|
def setStatusColours(self, new: list[dict], deleted: list[str]) -> bool:
|
||||||
"""Update the list of novel file status flags.
|
"""Update the list of novel file status flags."""
|
||||||
"""
|
return self._setStatusImport(new, deleted, self._data.itemStatus)
|
||||||
return self._setStatusImport(newCols, delCols, self._data.itemStatus)
|
|
||||||
|
|
||||||
def setImportColours(self, newCols, delCols):
|
def setImportColours(self, new: list[dict], deleted: list[str]) -> bool:
|
||||||
"""Update the list of note file importance flags.
|
"""Update the list of note file importance flags."""
|
||||||
"""
|
return self._setStatusImport(new, deleted, self._data.itemImport)
|
||||||
return self._setStatusImport(newCols, delCols, self._data.itemImport)
|
|
||||||
|
|
||||||
def setProjectChanged(self, value):
|
def setProjectChanged(self, status: bool) -> bool:
|
||||||
"""Toggle the project changed flag, and propagate the
|
"""Toggle the project changed flag, and propagate the
|
||||||
information to the GUI statusbar.
|
information to the GUI statusbar.
|
||||||
"""
|
"""
|
||||||
if isinstance(value, bool):
|
if isinstance(status, bool):
|
||||||
self._projChanged = value
|
self._projChanged = status
|
||||||
self.projectStatusChanged.emit(self._projChanged)
|
self.projectStatusChanged.emit(self._projChanged)
|
||||||
return self._projChanged
|
return self._projChanged
|
||||||
|
|
||||||
@@ -536,7 +536,7 @@ class NWProject(QObject):
|
|||||||
"""
|
"""
|
||||||
return self._data.editTime + round(time() - self._session.start)
|
return self._data.editTime + round(time() - self._session.start)
|
||||||
|
|
||||||
def getProjectItems(self):
|
def getProjectItems(self) -> Iterator[NWItem]:
|
||||||
"""This function ensures that the item tree loaded is sent to
|
"""This function ensures that the item tree loaded is sent to
|
||||||
the GUI tree view in such a way that the tree can be built. That
|
the GUI tree view in such a way that the tree can be built. That
|
||||||
is, the parent item must be sent before its child. In principle,
|
is, the parent item must be sent before its child. In principle,
|
||||||
@@ -577,19 +577,19 @@ class NWProject(QObject):
|
|||||||
logger.error("Item '%s' has no parent in current tree", tHandle)
|
logger.error("Item '%s' has no parent in current tree", tHandle)
|
||||||
tItem.setParent(None)
|
tItem.setParent(None)
|
||||||
yield tItem
|
yield tItem
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Class Methods
|
# Class Methods
|
||||||
##
|
##
|
||||||
|
|
||||||
def updateWordCounts(self):
|
def updateWordCounts(self) -> None:
|
||||||
"""Update the total word count values.
|
"""Update the total word count values."""
|
||||||
"""
|
|
||||||
novel, notes = self._tree.sumWords()
|
novel, notes = self._tree.sumWords()
|
||||||
self._data.setCurrCounts(novel=novel, notes=notes)
|
self._data.setCurrCounts(novel=novel, notes=notes)
|
||||||
return
|
return
|
||||||
|
|
||||||
def countStatus(self):
|
def countStatus(self) -> None:
|
||||||
"""Count how many times the various status flags are used in the
|
"""Count how many times the various status flags are used in the
|
||||||
project tree. The counts themselves are kept in the NWStatus
|
project tree. The counts themselves are kept in the NWStatus
|
||||||
objects. This is essentially a refresh.
|
objects. This is essentially a refresh.
|
||||||
@@ -603,18 +603,18 @@ class NWProject(QObject):
|
|||||||
self._data.itemImport.increment(nwItem.itemImport)
|
self._data.itemImport.increment(nwItem.itemImport)
|
||||||
return
|
return
|
||||||
|
|
||||||
def localLookup(self, theWord):
|
def localLookup(self, word: str | int) -> str:
|
||||||
"""Look up a word in the translation map for the project and
|
"""Look up a word or number in the translation map for the
|
||||||
return it. The variable is cast to a string before lookup. If
|
project and return it. The variable is cast to a string before
|
||||||
the word does not exist, it returns itself.
|
lookup. If the word does not exist, it returns itself.
|
||||||
"""
|
"""
|
||||||
return self._langData.get(str(theWord), str(theWord))
|
return self._langData.get(str(word), str(word))
|
||||||
|
|
||||||
##
|
##
|
||||||
# Internal Functions
|
# Internal Functions
|
||||||
##
|
##
|
||||||
|
|
||||||
def _setStatusImport(self, new, delete, target):
|
def _setStatusImport(self, new: list[dict], delete: list[str], target: NWStatus) -> bool:
|
||||||
"""Update the list of novel file status or importance flags, and
|
"""Update the list of novel file status or importance flags, and
|
||||||
delete those that have been requested deleted.
|
delete those that have been requested deleted.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ def testCoreIndex_LoadSave(monkeypatch, prjLipsum, mockGUI, tstPaths):
|
|||||||
assert "7a992350f3eb6" in theIndex._itemIndex
|
assert "7a992350f3eb6" in theIndex._itemIndex
|
||||||
|
|
||||||
# Finalise
|
# Finalise
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreIndex_LoadSave
|
# END Test testCoreIndex_LoadSave
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ def testCoreIndex_ScanThis(mockGUI):
|
|||||||
assert theBits == ["@tag", "this", "and this"]
|
assert theBits == ["@tag", "this", "and this"]
|
||||||
assert thePos == [0, 6, 12]
|
assert thePos == [0, 6, 12]
|
||||||
|
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreIndex_ScanThis
|
# END Test testCoreIndex_ScanThis
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd):
|
|||||||
assert theIndex.checkThese(["@who", "Jane", "John"], cItem) == [False, False, False]
|
assert theIndex.checkThese(["@who", "Jane", "John"], cItem) == [False, False, False]
|
||||||
assert theIndex.checkThese(["@pov", "Jane", "John"], nItem) == [True, True, False]
|
assert theIndex.checkThese(["@pov", "Jane", "John"], nItem) == [True, True, False]
|
||||||
|
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreIndex_CheckThese
|
# END Test testCoreIndex_CheckThese
|
||||||
|
|
||||||
@@ -494,7 +494,7 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd):
|
|||||||
assert theIndex._itemIndex[pHandle]["T0000"].paraCount == 1
|
assert theIndex._itemIndex[pHandle]["T0000"].paraCount == 1
|
||||||
assert theIndex._itemIndex[pHandle]["T0000"].synopsis == ""
|
assert theIndex._itemIndex[pHandle]["T0000"].synopsis == ""
|
||||||
|
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreIndex_ScanText
|
# END Test testCoreIndex_ScanText
|
||||||
|
|
||||||
@@ -774,7 +774,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd):
|
|||||||
|
|
||||||
assert theIndex.saveIndex() is True
|
assert theIndex.saveIndex() is True
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreIndex_ExtractData
|
# END Test testCoreIndex_ExtractData
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def testCoreProject_NewRoot(fncPath, tstPaths, mockGUI, mockRnd):
|
|||||||
|
|
||||||
assert theProject.projChanged is True
|
assert theProject.projChanged is True
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
copyfile(projFile, testFile)
|
copyfile(projFile, testFile)
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||||
@@ -154,7 +154,7 @@ def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockR
|
|||||||
assert "0000000000011" not in theProject.tree
|
assert "0000000000011" not in theProject.tree
|
||||||
assert "0000000000012" not in theProject.tree
|
assert "0000000000012" not in theProject.tree
|
||||||
|
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreProject_NewFileFolder
|
# END Test testCoreProject_NewFileFolder
|
||||||
|
|
||||||
@@ -182,12 +182,12 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd):
|
|||||||
caplog.clear()
|
caplog.clear()
|
||||||
assert theProject.openProject(fncPath) is True
|
assert theProject.openProject(fncPath) is True
|
||||||
assert "Failed to check lock file" in caplog.text
|
assert "Failed to check lock file" in caplog.text
|
||||||
assert theProject.closeProject()
|
theProject.closeProject()
|
||||||
|
|
||||||
# Force open with lockfile
|
# Force open with lockfile
|
||||||
assert theProject._storage.writeLockFile()
|
assert theProject._storage.writeLockFile()
|
||||||
assert theProject.openProject(fncPath, overrideLock=True) is True
|
assert theProject.openProject(fncPath, overrideLock=True) is True
|
||||||
assert theProject.closeProject()
|
theProject.closeProject()
|
||||||
assert theProject.getLockStatus() is None
|
assert theProject.getLockStatus() is None
|
||||||
|
|
||||||
# Fail getting xml reader
|
# Fail getting xml reader
|
||||||
@@ -237,7 +237,7 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd):
|
|||||||
mp.setattr("novelwriter.core.tree.NWTree.updateItemData", lambda *a: False)
|
mp.setattr("novelwriter.core.tree.NWTree.updateItemData", lambda *a: False)
|
||||||
assert theProject.openProject(fncPath) is True
|
assert theProject.openProject(fncPath) is True
|
||||||
|
|
||||||
assert theProject.closeProject()
|
theProject.closeProject()
|
||||||
|
|
||||||
# Trigger an index rebuild
|
# Trigger an index rebuild
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
@@ -249,7 +249,7 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd):
|
|||||||
assert "The file format of your project is about to be" in mockGUI.lastQuestion[1]
|
assert "The file format of your project is about to be" in mockGUI.lastQuestion[1]
|
||||||
assert theProject.index._indexBroken is False
|
assert theProject.index._indexBroken is False
|
||||||
|
|
||||||
assert theProject.closeProject()
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreProject_Open
|
# END Test testCoreProject_Open
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncPath):
|
|||||||
# Save with and without autosave
|
# Save with and without autosave
|
||||||
assert theProject.saveProject(autoSave=False) is True
|
assert theProject.saveProject(autoSave=False) is True
|
||||||
assert theProject.saveProject(autoSave=True) is True
|
assert theProject.saveProject(autoSave=True) is True
|
||||||
assert theProject.closeProject()
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreProject_Save
|
# END Test testCoreProject_Save
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
|
|||||||
C.hWorldRoot,
|
C.hWorldRoot,
|
||||||
]
|
]
|
||||||
assert theProject.tree.handles() == oldOrder
|
assert theProject.tree.handles() == oldOrder
|
||||||
assert theProject.setTreeOrder(newOrder)
|
theProject.setTreeOrder(newOrder)
|
||||||
assert theProject.tree.handles() == newOrder
|
assert theProject.tree.handles() == newOrder
|
||||||
|
|
||||||
# Add a non-existing item
|
# Add a non-existing item
|
||||||
@@ -451,7 +451,7 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
|
|||||||
assert len(theProject.data.itemStatus) == 0
|
assert len(theProject.data.itemStatus) == 0
|
||||||
assert len(theProject.data.itemImport) == 0
|
assert len(theProject.data.itemImport) == 0
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# END Test testCoreProject_StatusImport
|
# END Test testCoreProject_StatusImport
|
||||||
|
|
||||||
@@ -509,9 +509,9 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
|||||||
# Project Language
|
# Project Language
|
||||||
theProject.setProjectChanged(False)
|
theProject.setProjectChanged(False)
|
||||||
theProject.data.setLanguage("en")
|
theProject.data.setLanguage("en")
|
||||||
assert theProject.setProjectLang(None) is True
|
theProject.setProjectLang(None)
|
||||||
assert theProject.data.language is None
|
assert theProject.data.language is None
|
||||||
assert theProject.setProjectLang("en_GB") is True
|
theProject.setProjectLang("en_GB")
|
||||||
assert theProject.data.language == "en_GB"
|
assert theProject.data.language == "en_GB"
|
||||||
|
|
||||||
# Language Lookup
|
# Language Lookup
|
||||||
@@ -562,9 +562,9 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
|
|||||||
"000000000000e", "000000000000f",
|
"000000000000e", "000000000000f",
|
||||||
]
|
]
|
||||||
assert theProject.tree.handles() == oldOrder
|
assert theProject.tree.handles() == oldOrder
|
||||||
assert theProject.setTreeOrder(newOrder)
|
theProject.setTreeOrder(newOrder)
|
||||||
assert theProject.tree.handles() == newOrder
|
assert theProject.tree.handles() == newOrder
|
||||||
assert theProject.setTreeOrder(oldOrder)
|
theProject.setTreeOrder(oldOrder)
|
||||||
assert theProject.tree.handles() == oldOrder
|
assert theProject.tree.handles() == oldOrder
|
||||||
|
|
||||||
# END Test testCoreProject_Methods
|
# END Test testCoreProject_Methods
|
||||||
@@ -590,7 +590,7 @@ def testCoreProject_OrphanedFiles(mockGUI, prjLipsum):
|
|||||||
|
|
||||||
# Save and close
|
# Save and close
|
||||||
assert theProject.saveProject() is True
|
assert theProject.saveProject() is True
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# First Item with Meta Data
|
# First Item with Meta Data
|
||||||
orphPath = prjLipsum / "content" / "636b6aa9b697b.nwd"
|
orphPath = prjLipsum / "content" / "636b6aa9b697b.nwd"
|
||||||
@@ -645,7 +645,7 @@ def testCoreProject_OrphanedFiles(mockGUI, prjLipsum):
|
|||||||
assert oItem.itemLayout == nwItemLayout.NOTE
|
assert oItem.itemLayout == nwItemLayout.NOTE
|
||||||
|
|
||||||
assert theProject.saveProject(prjLipsum)
|
assert theProject.saveProject(prjLipsum)
|
||||||
assert theProject.closeProject()
|
theProject.closeProject()
|
||||||
|
|
||||||
# Finally, check that the orphaned files function returns
|
# Finally, check that the orphaned files function returns
|
||||||
# if no project is open and no path is set
|
# if no project is open and no path is set
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ def testCoreStorage_OpenProjectInPlace(mockGUI, fncPath, mockRnd):
|
|||||||
assert storage.getXmlWriter() is None
|
assert storage.getXmlWriter() is None
|
||||||
assert bool(storage.getDocument(C.hSceneDoc)) is False
|
assert bool(storage.getDocument(C.hSceneDoc)) is False
|
||||||
assert storage.getMetaFile("file") is None
|
assert storage.getMetaFile("file") is None
|
||||||
|
assert storage.scanContent() == []
|
||||||
|
|
||||||
# Open project as a new project should fail
|
# Open project as a new project should fail
|
||||||
assert storage.openProjectInPlace(fncPath, newProject=True) is False
|
assert storage.openProjectInPlace(fncPath, newProject=True) is False
|
||||||
@@ -90,6 +91,9 @@ def testCoreStorage_OpenProjectInPlace(mockGUI, fncPath, mockRnd):
|
|||||||
assert isinstance(storage.getXmlReader(), ProjectXMLReader)
|
assert isinstance(storage.getXmlReader(), ProjectXMLReader)
|
||||||
assert isinstance(storage.getXmlWriter(), ProjectXMLWriter)
|
assert isinstance(storage.getXmlWriter(), ProjectXMLWriter)
|
||||||
|
|
||||||
|
# Get content
|
||||||
|
assert storage.scanContent() == [C.hTitlePage, C.hChapterDoc, C.hSceneDoc]
|
||||||
|
|
||||||
# Get document
|
# Get document
|
||||||
assert storage.getDocument(C.hSceneDoc).readDocument() == "### New Scene\n\n"
|
assert storage.getDocument(C.hSceneDoc).readDocument() == "### New Scene\n\n"
|
||||||
|
|
||||||
@@ -97,7 +101,7 @@ def testCoreStorage_OpenProjectInPlace(mockGUI, fncPath, mockRnd):
|
|||||||
assert storage.getMetaFile("stuff") == fncPath / "meta" / "stuff"
|
assert storage.getMetaFile("stuff") == fncPath / "meta" / "stuff"
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
assert theProject.closeProject() is True
|
theProject.closeProject()
|
||||||
|
|
||||||
# Check closed project return values (again)
|
# Check closed project return values (again)
|
||||||
assert storage.isOpen() is False
|
assert storage.isOpen() is False
|
||||||
|
|||||||
Reference in New Issue
Block a user