diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 5e02cb83..a999e65c 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -240,50 +240,50 @@ class NWIndex: # Index Building ## - def scanText(self, tHandle: str, theText: str) -> bool: + def scanText(self, tHandle: str, text: str) -> bool: """Scan a piece of text associated with a handle. This will update the indices accordingly. This function takes the handle and text as separate inputs as we want to primarily scan the files before we save them, in which case we already have the text. """ - theItem = self._project.tree[tHandle] - if theItem is None: + tItem = self._project.tree[tHandle] + if tItem is None: logger.info("Not indexing unknown item '%s'", tHandle) return False - if not theItem.isFileType(): + if not tItem.isFileType(): logger.info("Not indexing non-file item '%s'", tHandle) return False # Keep a record of existing tags, and create a new item entry itemTags = dict.fromkeys(self._itemIndex.allItemTags(tHandle), False) - self._itemIndex.add(tHandle, theItem) + self._itemIndex.add(tHandle, tItem) # Run word counter for the whole text - cC, wC, pC = countWords(theText) - theItem.setCharCount(cC) - theItem.setWordCount(wC) - theItem.setParaCount(pC) + cC, wC, pC = countWords(text) + tItem.setCharCount(cC) + tItem.setWordCount(wC) + tItem.setParaCount(pC) # If the file's meta data is missing, or the file is out of the # main project, we don't index the content - if theItem.itemLayout == nwItemLayout.NO_LAYOUT: + if tItem.itemLayout == nwItemLayout.NO_LAYOUT: logger.info("Not indexing no-layout item '%s'", tHandle) return False - if theItem.itemParent is None: + if tItem.itemParent is None: logger.info("Not indexing orphaned item '%s'", tHandle) return False logger.debug("Indexing item with handle '%s'", tHandle) - if theItem.isInactiveClass(): - self._scanInactive(theItem, theText) + if tItem.isInactiveClass(): + self._scanInactive(tItem, text) else: - self._scanActive(tHandle, theItem, theText, itemTags) + self._scanActive(tHandle, tItem, text, itemTags) # Update timestamps for index changes nowTime = time() self._indexChange = nowTime - self._rootChange[theItem.itemRoot] = nowTime + self._rootChange[tItem.itemRoot] = nowTime return True @@ -479,9 +479,8 @@ class NWIndex: # If we're still here, we check that the references exist refKey = nwKeyWords.KEY_CLASS[tBits[0]].name for n in range(1, nBits): - tagKey = tBits[n].lower() - if tagKey in self._tagsIndex: - isGood[n] = self._tagsIndex.tagClass(tagKey) == refKey + if tBits[n] in self._tagsIndex: + isGood[n] = self._tagsIndex.tagClass(tBits[n]) == refKey return isGood @@ -641,30 +640,30 @@ class TagsIndex: __slots__ = ("_tags") - def __init__(self): + def __init__(self) -> None: self._tags: dict[str, dict] = {} return - def __contains__(self, tagKey): - return tagKey in self._tags + def __contains__(self, tagKey: str) -> bool: + return tagKey.lower() in self._tags - def __delitem__(self, tagKey): - self._tags.pop(tagKey, None) + def __delitem__(self, tagKey: str) -> None: + self._tags.pop(tagKey.lower(), None) return - def __getitem__(self, tagKey): - return self._tags.get(tagKey, None) + def __getitem__(self, tagKey: str) -> dict | None: + return self._tags.get(tagKey.lower(), None) ## # Methods ## - def clear(self): + def clear(self) -> None: """Clear the index.""" self._tags = {} return - def add(self, tagKey: str, tHandle: str, sTitle: str, itemClass: nwItemClass): + def add(self, tagKey: str, tHandle: str, sTitle: str, itemClass: nwItemClass) -> None: """Add a key to the index and set all values.""" self._tags[tagKey.lower()] = { "name": tagKey, "handle": tHandle, "heading": sTitle, "class": itemClass.name @@ -673,7 +672,7 @@ class TagsIndex: def tagName(self, tagKey: str) -> str: """Get the display name of a given tag.""" - return self._tags.get(tagKey.lower(), {}).get("name", None) + return self._tags.get(tagKey.lower(), {}).get("name", "") def tagHandle(self, tagKey: str) -> str: """Get the handle of a given tag.""" @@ -695,7 +694,7 @@ class TagsIndex: """Pack all the data of the tags into a single dictionary.""" return self._tags - def unpackData(self, data: dict): + def unpackData(self, data: dict) -> None: """Iterate through the tagsIndex loaded from cache and check that it's valid. """ @@ -714,7 +713,7 @@ class TagsIndex: raise KeyError("A tagIndex item is missing a heading entry") if "class" not in tagData: raise KeyError("A tagIndex item is missing a class entry") - if tagData["name"].lower() != tagKey.lower(): + if tagData["name"].lower() != tagKey: raise ValueError("tagsIndex name must match key") if not isHandle(tagData["handle"]): raise ValueError("tagsIndex handle must be a handle") diff --git a/tests/reference/coreIndex_LoadSave_tagsIndex.json b/tests/reference/coreIndex_LoadSave_tagsIndex.json index 63be8658..014346af 100644 --- a/tests/reference/coreIndex_LoadSave_tagsIndex.json +++ b/tests/reference/coreIndex_LoadSave_tagsIndex.json @@ -1,8 +1,8 @@ { "novelWriter.tagsIndex": { - "Bod": {"handle": "4c4f28287af27", "heading": "T0001", "class": "CHARACTER"}, - "Main": {"handle": "2426c6f0ca922", "heading": "T0001", "class": "PLOT"}, - "Europe": {"handle": "04468803b92e1", "heading": "T0001", "class": "WORLD"} + "bod": {"name": "Bod", "handle": "4c4f28287af27", "heading": "T0001", "class": "CHARACTER"}, + "main": {"name": "Main", "handle": "2426c6f0ca922", "heading": "T0001", "class": "PLOT"}, + "europe": {"name": "Europe", "handle": "04468803b92e1", "heading": "T0001", "class": "WORLD"} }, "novelWriter.itemIndex": { "7a992350f3eb6": { @@ -30,7 +30,7 @@ "T0001": {"level": "H2", "title": "Chapter One", "line": 1, "tag": "", "cCount": 419, "wCount": 67, "pCount": 1, "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at aliquam quam."} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "88243afbe5ed8": { @@ -39,7 +39,7 @@ "T0002": {"level": "H4", "title": "Scene One, Section Two", "line": 13, "tag": "", "cCount": 1561, "wCount": 230, "pCount": 2, "synopsis": ""} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "f96ec11c6a3da": { @@ -48,7 +48,7 @@ "T0002": {"level": "H4", "title": "Scene Two, Section Two", "line": 15, "tag": "", "cCount": 2009, "wCount": 301, "pCount": 3, "synopsis": ""} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "846352075de7d": { @@ -61,7 +61,7 @@ "T0001": {"level": "H2", "title": "Chapter Two", "line": 1, "tag": "", "cCount": 477, "wCount": 70, "pCount": 1, "synopsis": "Curabitur a elit posuere, varius ex et, convallis neque. Phasellus sagittis pharetra sem vitae dapibus. Curabitur varius lorem non pulvinar congue."} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "eb103bc70c90c": { @@ -69,7 +69,7 @@ "T0001": {"level": "H3", "title": "Scene Three", "line": 1, "tag": "", "cCount": 3006, "wCount": 439, "pCount": 4, "synopsis": "Aenean ut libero ut lectus porttitor rhoncus vel et massa. Nam pretium, nibh et varius vehicula, urna metus blandit eros, euismod pharetra diam diam et libero. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos."} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "f8c0562e50f1b": { @@ -77,7 +77,7 @@ "T0001": {"level": "H3", "title": "Scene Four", "line": 1, "tag": "", "cCount": 3839, "wCount": 563, "pCount": 6, "synopsis": "Nam tempor blandit magna laoreet aliquet. Vestibulum auctor posuere leo, ac gravida nisi rhoncus varius. Aenean posuere dolor vitae condimentum volutpat. Donec egestas volutpat risus, quis luctus justo."} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "47666c91c7ccf": { @@ -85,25 +85,25 @@ "T0001": {"level": "H3", "title": "Scene Five", "line": 1, "tag": "", "cCount": 3644, "wCount": 543, "pCount": 5, "synopsis": "Praesent eget est porta, dictum ante in, egestas risus. Mauris risus mauris, consequat aliquam mauris et, feugiat iaculis ipsum. Aliquam arcu ipsum, fermentum ut arcu sed, lobortis euismod sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus."} }, "references": { - "T0001": {"Bod": "@pov", "Main": "@plot", "Europe": "@location"} + "T0001": {"bod": "@pov", "main": "@plot", "europe": "@location"} } }, "4c4f28287af27": { "headings": { - "T0001": {"level": "H1", "title": "Nobody Owens", "line": 1, "tag": "Bod", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""} + "T0001": {"level": "H1", "title": "Nobody Owens", "line": 1, "tag": "bod", "cCount": 1864, "wCount": 284, "pCount": 3, "synopsis": ""} }, "references": { - "T0001": {"Main": "@plot"} + "T0001": {"main": "@plot"} } }, "2426c6f0ca922": { "headings": { - "T0001": {"level": "H1", "title": "Main Plot", "line": 1, "tag": "Main", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""} + "T0001": {"level": "H1", "title": "Main Plot", "line": 1, "tag": "main", "cCount": 1369, "wCount": 195, "pCount": 2, "synopsis": ""} } }, "04468803b92e1": { "headings": { - "T0001": {"level": "H1", "title": "Ancient Europe", "line": 1, "tag": "Europe", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""} + "T0001": {"level": "H1", "title": "Ancient Europe", "line": 1, "tag": "europe", "cCount": 1770, "wCount": 259, "pCount": 3, "synopsis": ""} } } } diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index b9e107e5..40557584 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -25,6 +25,7 @@ import pytest from shutil import copyfile from mocked import causeException +from novelwriter.core.item import NWItem from tools import C, buildTestProject, cmpFiles, writeFile from novelwriter.enum import nwItemClass, nwItemLayout @@ -42,11 +43,11 @@ def testCoreIndex_LoadSave(monkeypatch, prjLipsum, mockGUI, tstPaths): testFile = tstPaths.outDir / "coreIndex_LoadSave_tagsIndex.json" compFile = tstPaths.refDir / "coreIndex_LoadSave_tagsIndex.json" - theProject = NWProject() - assert theProject.openProject(prjLipsum) + project = NWProject() + assert project.openProject(prjLipsum) - theIndex = NWIndex(theProject) - assert repr(theIndex) == "" + index = NWIndex(project) + assert repr(index) == "" notIndexable = { "b3643d0f92e32": False, # Novel ROOT @@ -56,98 +57,98 @@ def testCoreIndex_LoadSave(monkeypatch, prjLipsum, mockGUI, tstPaths): "6c6afb1247750": False, # Plot ROOT "60bdf227455cc": False, # World ROOT } - for tItem in theProject.tree: - assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(tItem.itemHandle, True) + for tItem in project.tree: + assert index.reIndexHandle(tItem.itemHandle) is notIndexable.get(tItem.itemHandle, True) - assert theIndex.reIndexHandle(None) is False + assert index.reIndexHandle(None) is False # No folder for saving with monkeypatch.context() as mp: mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None) - assert theIndex.saveIndex() is False + assert index.saveIndex() is False # Make the save fail with monkeypatch.context() as mp: mp.setattr("builtins.open", causeException) - assert theIndex.saveIndex() is False + assert index.saveIndex() is False # Make the save pass - assert theIndex.saveIndex() is True + assert index.saveIndex() is True # Take a copy of the index - tagIndex = str(theIndex._tagsIndex.packData()) - itemsIndex = str(theIndex._itemIndex.packData()) + tagIndex = str(index._tagsIndex.packData()) + itemsIndex = str(index._itemIndex.packData()) # Delete a handle - assert theIndex._tagsIndex["Bod"] is not None - assert theIndex._itemIndex["4c4f28287af27"] is not None - theIndex.deleteHandle("4c4f28287af27") - assert theIndex._tagsIndex["Bod"] is None - assert theIndex._itemIndex["4c4f28287af27"] is None + assert index._tagsIndex["Bod"] is not None + assert index._itemIndex["4c4f28287af27"] is not None + index.deleteHandle("4c4f28287af27") + assert index._tagsIndex["Bod"] is None + assert index._itemIndex["4c4f28287af27"] is None # Clear the index - theIndex.clearIndex() - assert theIndex._tagsIndex._tags == {} - assert theIndex._itemIndex._items == {} + index.clearIndex() + assert index._tagsIndex._tags == {} + assert index._itemIndex._items == {} # No folder for loading with monkeypatch.context() as mp: mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None) - assert theIndex.loadIndex() is False + assert index.loadIndex() is False # Make the load fail with monkeypatch.context() as mp: mp.setattr(json, "load", causeException) - assert theIndex.loadIndex() is False - assert theIndex.indexBroken is True + assert index.loadIndex() is False + assert index.indexBroken is True # Make the load pass - assert theIndex.loadIndex() is True - assert theIndex.indexBroken is False + assert index.loadIndex() is True + assert index.indexBroken is False - assert str(theIndex._tagsIndex.packData()) == tagIndex - assert str(theIndex._itemIndex.packData()) == itemsIndex + assert str(index._tagsIndex.packData()) == tagIndex + assert str(index._itemIndex.packData()) == itemsIndex # Rebuild index - theIndex.clearIndex() - theIndex.rebuildIndex() + index.clearIndex() + index.rebuildIndex() - assert str(theIndex._tagsIndex.packData()) == tagIndex - assert str(theIndex._itemIndex.packData()) == itemsIndex + assert str(index._tagsIndex.packData()) == tagIndex + assert str(index._itemIndex.packData()) == itemsIndex # Check File copyfile(projFile, testFile) assert cmpFiles(testFile, compFile) - # Write an emtpy index file and load it + # Write an empty index file and load it writeFile(projFile, "{}") - assert theIndex.loadIndex() is False - assert theIndex.indexBroken is True + assert index.loadIndex() is False + assert index.indexBroken is True # Write an index file that passes loading, but is still empty writeFile(projFile, '{"novelWriter.tagsIndex": {}, "novelWriter.itemIndex": {}}') - assert theIndex.loadIndex() is True - assert theIndex.indexBroken is False + assert index.loadIndex() is True + assert index.indexBroken is False # Check that the index is re-populated - assert "04468803b92e1" in theIndex._itemIndex - assert "2426c6f0ca922" in theIndex._itemIndex - assert "441420a886d82" in theIndex._itemIndex - assert "47666c91c7ccf" in theIndex._itemIndex - assert "4c4f28287af27" in theIndex._itemIndex - assert "846352075de7d" in theIndex._itemIndex - assert "88243afbe5ed8" in theIndex._itemIndex - assert "88d59a277361b" in theIndex._itemIndex - assert "8c58a65414c23" in theIndex._itemIndex - assert "db7e733775d4d" in theIndex._itemIndex - assert "eb103bc70c90c" in theIndex._itemIndex - assert "f8c0562e50f1b" in theIndex._itemIndex - assert "f96ec11c6a3da" in theIndex._itemIndex - assert "fb609cd8319dc" in theIndex._itemIndex - assert "7a992350f3eb6" in theIndex._itemIndex + assert "04468803b92e1" in index._itemIndex + assert "2426c6f0ca922" in index._itemIndex + assert "441420a886d82" in index._itemIndex + assert "47666c91c7ccf" in index._itemIndex + assert "4c4f28287af27" in index._itemIndex + assert "846352075de7d" in index._itemIndex + assert "88243afbe5ed8" in index._itemIndex + assert "88d59a277361b" in index._itemIndex + assert "8c58a65414c23" in index._itemIndex + assert "db7e733775d4d" in index._itemIndex + assert "eb103bc70c90c" in index._itemIndex + assert "f8c0562e50f1b" in index._itemIndex + assert "f96ec11c6a3da" in index._itemIndex + assert "fb609cd8319dc" in index._itemIndex + assert "7a992350f3eb6" in index._itemIndex # Finalise - theProject.closeProject() + project.closeProject() # END Test testCoreIndex_LoadSave @@ -155,85 +156,89 @@ def testCoreIndex_LoadSave(monkeypatch, prjLipsum, mockGUI, tstPaths): @pytest.mark.core def testCoreIndex_ScanThis(mockGUI): """Test the tag scanner function scanThis.""" - theProject = NWProject() - theIndex = theProject.index + project = NWProject() + index = project.index - isValid, theBits, thePos = theIndex.scanThis("tag: this, and this") + isValid, theBits, thePos = index.scanThis("tag: this, and this") assert isValid is False - isValid, theBits, thePos = theIndex.scanThis("@") + isValid, theBits, thePos = index.scanThis("@") assert isValid is False - isValid, theBits, thePos = theIndex.scanThis("@:") + isValid, theBits, thePos = index.scanThis("@:") assert isValid is False - isValid, theBits, thePos = theIndex.scanThis(" @a: b") + isValid, theBits, thePos = index.scanThis(" @a: b") assert isValid is False - isValid, theBits, thePos = theIndex.scanThis("@a:") + isValid, theBits, thePos = index.scanThis("@a:") assert isValid is True assert theBits == ["@a"] assert thePos == [0] - isValid, theBits, thePos = theIndex.scanThis("@a:b") + isValid, theBits, thePos = index.scanThis("@a:b") assert isValid is True assert theBits == ["@a", "b"] assert thePos == [0, 3] - isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d") + isValid, theBits, thePos = index.scanThis("@a:b,c,d") assert isValid is True assert theBits == ["@a", "b", "c", "d"] assert thePos == [0, 3, 5, 7] - isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d") + isValid, theBits, thePos = index.scanThis("@a : b , c , d") assert isValid is True assert theBits == ["@a", "b", "c", "d"] assert thePos == [0, 5, 9, 13] - isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this") + isValid, theBits, thePos = index.scanThis("@tag: this, and this") assert isValid is True assert theBits == ["@tag", "this", "and this"] assert thePos == [0, 6, 12] - theProject.closeProject() + project.closeProject() # END Test testCoreIndex_ScanThis @pytest.mark.core def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd): - """Test the tag checker function checkThese. - """ - theProject = NWProject() + """Test the tag checker function checkThese.""" + project = NWProject() mockRnd.reset() - buildTestProject(theProject, fncPath) - theIndex = theProject.index - theIndex.clearIndex() + buildTestProject(project, fncPath) + index = project.index + index.clearIndex() - nHandle = theProject.newFile("Hello", C.hNovelRoot) - cHandle = theProject.newFile("Jane", C.hCharRoot) - nItem = theProject.tree[nHandle] - cItem = theProject.tree[cHandle] + nHandle = project.newFile("Hello", C.hNovelRoot) + cHandle = project.newFile("Jane", C.hCharRoot) + assert isinstance(nHandle, str) + assert isinstance(cHandle, str) - assert theIndex.rootChangedSince(C.hNovelRoot, 0) is False - assert theIndex.indexChangedSince(0) is False + nItem = project.tree[nHandle] + cItem = project.tree[cHandle] + assert isinstance(nItem, NWItem) + assert isinstance(cItem, NWItem) - assert theIndex.scanText(cHandle, ( + assert index.rootChangedSince(C.hNovelRoot, 0) is False + assert index.indexChangedSince(0) is False + + assert index.scanText(cHandle, ( "# Jane Smith\n" "@tag: Jane\n" "@tag:\n" "@:\n" )) - assert theIndex.scanText(nHandle, ( + assert index.scanText(nHandle, ( "# Hello World!\n" "@pov: Jane\n" "@invalid: John\n" # Checks for issue #688 )) - assert theIndex._tagsIndex.tagHandle("Jane") == cHandle - assert theIndex._tagsIndex.tagHeading("Jane") == "T0001" - assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER" - assert theIndex.getItemHeader(nHandle, "T0001").title == "Hello World!" - assert theIndex.getReferences(nHandle, "T0001") == { + assert index._tagsIndex.tagHandle("Jane") == cHandle + assert index._tagsIndex.tagHeading("Jane") == "T0001" + assert index._tagsIndex.tagClass("Jane") == "CHARACTER" + assert index.getItemHeader(nHandle, "T0001").title == "Hello World!" # type: ignore + assert index.getReferences(nHandle, "T0001") == { "@char": [], "@custom": [], "@entity": [], @@ -245,35 +250,35 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd): "@time": [] } - assert theIndex.rootChangedSince(C.hNovelRoot, 0) is True - assert theIndex.indexChangedSince(0) is True + assert index.rootChangedSince(C.hNovelRoot, 0) is True + assert index.indexChangedSince(0) is True assert cItem.mainHeading == "H1" assert nItem.mainHeading == "H1" # Zero Items - assert theIndex.checkThese([], cItem) == [] + assert index.checkThese([], cItem) == [] # One Item - assert theIndex.checkThese(["@tag"], cItem) == [True] - assert theIndex.checkThese(["@who"], cItem) == [False] + assert index.checkThese(["@tag"], cItem) == [True] + assert index.checkThese(["@who"], cItem) == [False] # Two Items - assert theIndex.checkThese(["@tag", "Jane"], cItem) == [True, True] - assert theIndex.checkThese(["@tag", "John"], cItem) == [True, True] - assert theIndex.checkThese(["@tag", "Jane"], nItem) == [True, False] - assert theIndex.checkThese(["@tag", "John"], nItem) == [True, True] - assert theIndex.checkThese(["@pov", "John"], nItem) == [True, False] - assert theIndex.checkThese(["@pov", "Jane"], nItem) == [True, True] - assert theIndex.checkThese(["@ pov", "Jane"], nItem) == [False, False] - assert theIndex.checkThese(["@what", "Jane"], nItem) == [False, False] + assert index.checkThese(["@tag", "Jane"], cItem) == [True, True] + assert index.checkThese(["@tag", "John"], cItem) == [True, True] + assert index.checkThese(["@tag", "Jane"], nItem) == [True, False] + assert index.checkThese(["@tag", "John"], nItem) == [True, True] + assert index.checkThese(["@pov", "John"], nItem) == [True, False] + assert index.checkThese(["@pov", "Jane"], nItem) == [True, True] + assert index.checkThese(["@ pov", "Jane"], nItem) == [False, False] + assert index.checkThese(["@what", "Jane"], nItem) == [False, False] # Three Items - assert theIndex.checkThese(["@tag", "Jane", "John"], cItem) == [True, True, False] - assert theIndex.checkThese(["@who", "Jane", "John"], cItem) == [False, False, False] - assert theIndex.checkThese(["@pov", "Jane", "John"], nItem) == [True, True, False] + assert index.checkThese(["@tag", "Jane", "John"], cItem) == [True, True, False] + assert index.checkThese(["@who", "Jane", "John"], cItem) == [False, False, False] + assert index.checkThese(["@pov", "Jane", "John"], nItem) == [True, True, False] - theProject.closeProject() + project.closeProject() # END Test testCoreIndex_CheckThese @@ -281,60 +286,69 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd): @pytest.mark.core def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd): """Check the index text scanner.""" - theProject = NWProject() + project = NWProject() mockRnd.reset() - buildTestProject(theProject, fncPath) - theIndex = theProject.index + buildTestProject(project, fncPath) + index = project.index # Some items for fail to scan tests - dHandle = theProject.newFolder("Folder", C.hNovelRoot) - xHandle = theProject.newFile("No Layout", C.hNovelRoot) - xItem = theProject.tree[xHandle] + dHandle = project.newFolder("Folder", C.hNovelRoot) + xHandle = project.newFile("No Layout", C.hNovelRoot) + assert isinstance(dHandle, str) + assert isinstance(xHandle, str) + + xItem = project.tree[xHandle] + assert isinstance(xItem, NWItem) xItem.setLayout(nwItemLayout.NO_LAYOUT) # Check invalid data - assert theIndex.scanText(None, "Hello World!") is False - assert theIndex.scanText(dHandle, "Hello World!") is False - assert theIndex.scanText(xHandle, "Hello World!") is False + assert index.scanText(None, "Hello World!") is False # type: ignore + assert index.scanText(dHandle, "Hello World!") is False + assert index.scanText(xHandle, "Hello World!") is False xItem.setLayout(nwItemLayout.DOCUMENT) xItem.setParent(None) - assert theIndex.scanText(xHandle, "Hello World!") is False + assert index.scanText(xHandle, "Hello World!") is False # Create the trash folder - tHandle = theProject.trashFolder() - assert theProject.tree[tHandle] is not None + tHandle = project.trashFolder() + assert project.tree[tHandle] is not None xItem.setParent(tHandle) - theProject.tree.updateItemData(xItem.itemHandle) + project.tree.updateItemData(xItem.itemHandle) assert xItem.itemRoot == tHandle assert xItem.itemClass == nwItemClass.TRASH - assert theIndex.scanText(xHandle, "## Hello World!") is True + assert index.scanText(xHandle, "## Hello World!") is True assert xItem.mainHeading == "H2" # Create the archive root - aHandle = theProject.newRoot(nwItemClass.ARCHIVE) - assert theProject.tree[aHandle] is not None + aHandle = project.newRoot(nwItemClass.ARCHIVE) + assert project.tree[aHandle] is not None xItem.setParent(aHandle) - theProject.tree.updateItemData(xItem.itemHandle) - assert theIndex.scanText(xHandle, "### Hello World!") is True + project.tree.updateItemData(xItem.itemHandle) + assert index.scanText(xHandle, "### Hello World!") is True assert xItem.mainHeading == "H3" # Make some usable items - tHandle = theProject.newFile("Title", C.hNovelRoot) - pHandle = theProject.newFile("Page", C.hNovelRoot) - nHandle = theProject.newFile("Hello", C.hNovelRoot) - cHandle = theProject.newFile("Jane", C.hCharRoot) - sHandle = theProject.newFile("Scene", C.hNovelRoot) + tHandle = project.newFile("Title", C.hNovelRoot) + pHandle = project.newFile("Page", C.hNovelRoot) + nHandle = project.newFile("Hello", C.hNovelRoot) + cHandle = project.newFile("Jane", C.hCharRoot) + sHandle = project.newFile("Scene", C.hNovelRoot) + assert isinstance(tHandle, str) + assert isinstance(pHandle, str) + assert isinstance(nHandle, str) + assert isinstance(cHandle, str) + assert isinstance(sHandle, str) # Text Indexing # ============= # Index correct text - assert theIndex.scanText(cHandle, ( + assert index.scanText(cHandle, ( "# Jane Smith\n" "@tag: Jane\n" )) - assert theIndex.scanText(nHandle, ( + assert index.scanText(nHandle, ( "# Hello World!\n" "@pov: Jane\n" "@char: Jane\n\n" @@ -342,16 +356,16 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd): "This is a story about Jane Smith.\n\n" "Well, not really.\n" )) - assert theIndex._tagsIndex.tagHandle("Jane") == cHandle - assert theIndex._tagsIndex.tagHeading("Jane") == "T0001" - assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER" - assert theIndex.getItemHeader(nHandle, "T0001").title == "Hello World!" + assert index._tagsIndex.tagHandle("Jane") == cHandle + assert index._tagsIndex.tagHeading("Jane") == "T0001" + assert index._tagsIndex.tagClass("Jane") == "CHARACTER" + assert index.getItemHeader(nHandle, "T0001").title == "Hello World!" # type: ignore # Title Indexing # ============== # Document File - assert theIndex.scanText(nHandle, ( + assert index.scanText(nHandle, ( "# Title One\n\n" "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n" @@ -367,64 +381,64 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd): "##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word "Paragraph Five.\n\n" )) - assert theIndex._itemIndex[nHandle]["T0001"].references == {} - assert theIndex._itemIndex[nHandle]["T0002"].references == {} - assert theIndex._itemIndex[nHandle]["T0003"].references == {} - assert theIndex._itemIndex[nHandle]["T0004"].references == {} + assert index._itemIndex[nHandle]["T0001"].references == {} # type: ignore + assert index._itemIndex[nHandle]["T0002"].references == {} # type: ignore + assert index._itemIndex[nHandle]["T0003"].references == {} # type: ignore + assert index._itemIndex[nHandle]["T0004"].references == {} # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].level == "H1" - assert theIndex._itemIndex[nHandle]["T0002"].level == "H2" - assert theIndex._itemIndex[nHandle]["T0003"].level == "H3" - assert theIndex._itemIndex[nHandle]["T0004"].level == "H4" + assert index._itemIndex[nHandle]["T0001"].level == "H1" # type: ignore + assert index._itemIndex[nHandle]["T0002"].level == "H2" # type: ignore + assert index._itemIndex[nHandle]["T0003"].level == "H3" # type: ignore + assert index._itemIndex[nHandle]["T0004"].level == "H4" # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].line == 1 - assert theIndex._itemIndex[nHandle]["T0002"].line == 7 - assert theIndex._itemIndex[nHandle]["T0003"].line == 13 - assert theIndex._itemIndex[nHandle]["T0004"].line == 19 + assert index._itemIndex[nHandle]["T0001"].line == 1 # type: ignore + assert index._itemIndex[nHandle]["T0002"].line == 7 # type: ignore + assert index._itemIndex[nHandle]["T0003"].line == 13 # type: ignore + assert index._itemIndex[nHandle]["T0004"].line == 19 # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].title == "Title One" - assert theIndex._itemIndex[nHandle]["T0002"].title == "Title Two" - assert theIndex._itemIndex[nHandle]["T0003"].title == "Title Three" - assert theIndex._itemIndex[nHandle]["T0004"].title == "Title Four" + assert index._itemIndex[nHandle]["T0001"].title == "Title One" # type: ignore + assert index._itemIndex[nHandle]["T0002"].title == "Title Two" # type: ignore + assert index._itemIndex[nHandle]["T0003"].title == "Title Three" # type: ignore + assert index._itemIndex[nHandle]["T0004"].title == "Title Four" # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].charCount == 23 - assert theIndex._itemIndex[nHandle]["T0002"].charCount == 23 - assert theIndex._itemIndex[nHandle]["T0003"].charCount == 27 - assert theIndex._itemIndex[nHandle]["T0004"].charCount == 56 + assert index._itemIndex[nHandle]["T0001"].charCount == 23 # type: ignore + assert index._itemIndex[nHandle]["T0002"].charCount == 23 # type: ignore + assert index._itemIndex[nHandle]["T0003"].charCount == 27 # type: ignore + assert index._itemIndex[nHandle]["T0004"].charCount == 56 # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].wordCount == 4 - assert theIndex._itemIndex[nHandle]["T0002"].wordCount == 4 - assert theIndex._itemIndex[nHandle]["T0003"].wordCount == 4 - assert theIndex._itemIndex[nHandle]["T0004"].wordCount == 9 + assert index._itemIndex[nHandle]["T0001"].wordCount == 4 # type: ignore + assert index._itemIndex[nHandle]["T0002"].wordCount == 4 # type: ignore + assert index._itemIndex[nHandle]["T0003"].wordCount == 4 # type: ignore + assert index._itemIndex[nHandle]["T0004"].wordCount == 9 # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].paraCount == 1 - assert theIndex._itemIndex[nHandle]["T0002"].paraCount == 1 - assert theIndex._itemIndex[nHandle]["T0003"].paraCount == 1 - assert theIndex._itemIndex[nHandle]["T0004"].paraCount == 3 + assert index._itemIndex[nHandle]["T0001"].paraCount == 1 # type: ignore + assert index._itemIndex[nHandle]["T0002"].paraCount == 1 # type: ignore + assert index._itemIndex[nHandle]["T0003"].paraCount == 1 # type: ignore + assert index._itemIndex[nHandle]["T0004"].paraCount == 3 # type: ignore - assert theIndex._itemIndex[nHandle]["T0001"].synopsis == "Synopsis One." - assert theIndex._itemIndex[nHandle]["T0002"].synopsis == "Synopsis Two." - assert theIndex._itemIndex[nHandle]["T0003"].synopsis == "Synopsis Three." - assert theIndex._itemIndex[nHandle]["T0004"].synopsis == "Synopsis Four." + assert index._itemIndex[nHandle]["T0001"].synopsis == "Synopsis One." # type: ignore + assert index._itemIndex[nHandle]["T0002"].synopsis == "Synopsis Two." # type: ignore + assert index._itemIndex[nHandle]["T0003"].synopsis == "Synopsis Three." # type: ignore + assert index._itemIndex[nHandle]["T0004"].synopsis == "Synopsis Four." # type: ignore # Note File - assert theIndex.scanText(cHandle, ( + assert index.scanText(cHandle, ( "# Title One\n\n" "@tag: One\n\n" "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n" )) - assert theIndex._itemIndex[cHandle]["T0001"].references == {} - assert theIndex._itemIndex[cHandle]["T0001"].level == "H1" - assert theIndex._itemIndex[cHandle]["T0001"].line == 1 - assert theIndex._itemIndex[cHandle]["T0001"].title == "Title One" - assert theIndex._itemIndex[cHandle]["T0001"].charCount == 23 - assert theIndex._itemIndex[cHandle]["T0001"].wordCount == 4 - assert theIndex._itemIndex[cHandle]["T0001"].paraCount == 1 - assert theIndex._itemIndex[cHandle]["T0001"].synopsis == "Synopsis One." + assert index._itemIndex[cHandle]["T0001"].references == {} # type: ignore + assert index._itemIndex[cHandle]["T0001"].level == "H1" # type: ignore + assert index._itemIndex[cHandle]["T0001"].line == 1 # type: ignore + assert index._itemIndex[cHandle]["T0001"].title == "Title One" # type: ignore + assert index._itemIndex[cHandle]["T0001"].charCount == 23 # type: ignore + assert index._itemIndex[cHandle]["T0001"].wordCount == 4 # type: ignore + assert index._itemIndex[cHandle]["T0001"].paraCount == 1 # type: ignore + assert index._itemIndex[cHandle]["T0001"].synopsis == "Synopsis One." # type: ignore # Valid and Invalid References - assert theIndex.scanText(sHandle, ( + assert index.scanText(sHandle, ( "# Title One\n\n" "@pov: One\n\n" # Valid "@char: Two\n\n" # Invalid tag @@ -432,69 +446,69 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd): "% synopsis: Synopsis One.\n\n" "Paragraph One.\n\n" )) - assert theIndex._itemIndex[sHandle]["T0001"].references == { - "One": {"@pov"}, "Two": {"@char"} + assert index._itemIndex[sHandle]["T0001"].references == { # type: ignore + "one": {"@pov"}, "two": {"@char"} } # Special Titles # ============== - assert theIndex.scanText(tHandle, ( + assert index.scanText(tHandle, ( "#! My Project\n\n" ">> By Jane Doe <<\n\n" )) - assert theIndex._itemIndex[cHandle]["T0001"].references == {} - assert theIndex._itemIndex[tHandle]["T0001"].level == "H1" - assert theIndex._itemIndex[tHandle]["T0001"].line == 1 - assert theIndex._itemIndex[tHandle]["T0001"].title == "My Project" - assert theIndex._itemIndex[tHandle]["T0001"].charCount == 21 - assert theIndex._itemIndex[tHandle]["T0001"].wordCount == 5 - assert theIndex._itemIndex[tHandle]["T0001"].paraCount == 1 - assert theIndex._itemIndex[tHandle]["T0001"].synopsis == "" + assert index._itemIndex[cHandle]["T0001"].references == {} # type: ignore + assert index._itemIndex[tHandle]["T0001"].level == "H1" # type: ignore + assert index._itemIndex[tHandle]["T0001"].line == 1 # type: ignore + assert index._itemIndex[tHandle]["T0001"].title == "My Project" # type: ignore + assert index._itemIndex[tHandle]["T0001"].charCount == 21 # type: ignore + assert index._itemIndex[tHandle]["T0001"].wordCount == 5 # type: ignore + assert index._itemIndex[tHandle]["T0001"].paraCount == 1 # type: ignore + assert index._itemIndex[tHandle]["T0001"].synopsis == "" # type: ignore - assert theIndex.scanText(tHandle, ( + assert index.scanText(tHandle, ( "##! Prologue\n\n" "In the beginning there was time ...\n\n" )) - assert theIndex._itemIndex[cHandle]["T0001"].references == {} - assert theIndex._itemIndex[tHandle]["T0001"].level == "H2" - assert theIndex._itemIndex[tHandle]["T0001"].line == 1 - assert theIndex._itemIndex[tHandle]["T0001"].title == "Prologue" - assert theIndex._itemIndex[tHandle]["T0001"].charCount == 43 - assert theIndex._itemIndex[tHandle]["T0001"].wordCount == 8 - assert theIndex._itemIndex[tHandle]["T0001"].paraCount == 1 - assert theIndex._itemIndex[tHandle]["T0001"].synopsis == "" + assert index._itemIndex[cHandle]["T0001"].references == {} # type: ignore + assert index._itemIndex[tHandle]["T0001"].level == "H2" # type: ignore + assert index._itemIndex[tHandle]["T0001"].line == 1 # type: ignore + assert index._itemIndex[tHandle]["T0001"].title == "Prologue" # type: ignore + assert index._itemIndex[tHandle]["T0001"].charCount == 43 # type: ignore + assert index._itemIndex[tHandle]["T0001"].wordCount == 8 # type: ignore + assert index._itemIndex[tHandle]["T0001"].paraCount == 1 # type: ignore + assert index._itemIndex[tHandle]["T0001"].synopsis == "" # type: ignore # Page wo/Title # ============= - theProject.tree[pHandle]._layout = nwItemLayout.DOCUMENT - assert theIndex.scanText(pHandle, ( + project.tree[pHandle]._layout = nwItemLayout.DOCUMENT # type: ignore + assert index.scanText(pHandle, ( "This is a page with some text on it.\n\n" )) - assert theIndex._itemIndex[pHandle]["T0000"].references == {} - assert theIndex._itemIndex[pHandle]["T0000"].level == "H0" - assert theIndex._itemIndex[pHandle]["T0000"].line == 0 - assert theIndex._itemIndex[pHandle]["T0000"].title == "" - assert theIndex._itemIndex[pHandle]["T0000"].charCount == 36 - assert theIndex._itemIndex[pHandle]["T0000"].wordCount == 9 - assert theIndex._itemIndex[pHandle]["T0000"].paraCount == 1 - assert theIndex._itemIndex[pHandle]["T0000"].synopsis == "" + assert index._itemIndex[pHandle]["T0000"].references == {} # type: ignore + assert index._itemIndex[pHandle]["T0000"].level == "H0" # type: ignore + assert index._itemIndex[pHandle]["T0000"].line == 0 # type: ignore + assert index._itemIndex[pHandle]["T0000"].title == "" # type: ignore + assert index._itemIndex[pHandle]["T0000"].charCount == 36 # type: ignore + assert index._itemIndex[pHandle]["T0000"].wordCount == 9 # type: ignore + assert index._itemIndex[pHandle]["T0000"].paraCount == 1 # type: ignore + assert index._itemIndex[pHandle]["T0000"].synopsis == "" # type: ignore - theProject.tree[pHandle]._layout = nwItemLayout.NOTE - assert theIndex.scanText(pHandle, ( + project.tree[pHandle]._layout = nwItemLayout.NOTE # type: ignore + assert index.scanText(pHandle, ( "This is a page with some text on it.\n\n" )) - assert theIndex._itemIndex[pHandle]["T0000"].references == {} - assert theIndex._itemIndex[pHandle]["T0000"].level == "H0" - assert theIndex._itemIndex[pHandle]["T0000"].line == 0 - assert theIndex._itemIndex[pHandle]["T0000"].title == "" - assert theIndex._itemIndex[pHandle]["T0000"].charCount == 36 - assert theIndex._itemIndex[pHandle]["T0000"].wordCount == 9 - assert theIndex._itemIndex[pHandle]["T0000"].paraCount == 1 - assert theIndex._itemIndex[pHandle]["T0000"].synopsis == "" + assert index._itemIndex[pHandle]["T0000"].references == {} # type: ignore + assert index._itemIndex[pHandle]["T0000"].level == "H0" # type: ignore + assert index._itemIndex[pHandle]["T0000"].line == 0 # type: ignore + assert index._itemIndex[pHandle]["T0000"].title == "" # type: ignore + assert index._itemIndex[pHandle]["T0000"].charCount == 36 # type: ignore + assert index._itemIndex[pHandle]["T0000"].wordCount == 9 # type: ignore + assert index._itemIndex[pHandle]["T0000"].paraCount == 1 # type: ignore + assert index._itemIndex[pHandle]["T0000"].synopsis == "" # type: ignore - theProject.closeProject() + project.closeProject() # END Test testCoreIndex_ScanText @@ -502,31 +516,33 @@ def testCoreIndex_ScanText(mockGUI, fncPath, mockRnd): @pytest.mark.core def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): """Check the index data extraction functions.""" - theProject = NWProject() + project = NWProject() mockRnd.reset() - buildTestProject(theProject, fncPath) + buildTestProject(project, fncPath) - theIndex = theProject.index - theIndex.reIndexHandle(C.hNovelRoot) - theIndex.reIndexHandle(C.hPlotRoot) - theIndex.reIndexHandle(C.hCharRoot) - theIndex.reIndexHandle(C.hWorldRoot) - theIndex.reIndexHandle(C.hTitlePage) - theIndex.reIndexHandle(C.hChapterDir) - theIndex.reIndexHandle(C.hChapterDoc) - theIndex.reIndexHandle(C.hSceneDoc) + index = project.index + index.reIndexHandle(C.hNovelRoot) + index.reIndexHandle(C.hPlotRoot) + index.reIndexHandle(C.hCharRoot) + index.reIndexHandle(C.hWorldRoot) + index.reIndexHandle(C.hTitlePage) + index.reIndexHandle(C.hChapterDir) + index.reIndexHandle(C.hChapterDoc) + index.reIndexHandle(C.hSceneDoc) - nHandle = theProject.newFile("Hello", C.hNovelRoot) - cHandle = theProject.newFile("Jane", C.hCharRoot) + nHandle = project.newFile("Hello", C.hNovelRoot) + cHandle = project.newFile("Jane", C.hCharRoot) + assert isinstance(nHandle, str) + assert isinstance(cHandle, str) - assert theIndex.getItemHeader("", "") is None - assert theIndex.getItemHeader(C.hNovelRoot, "") is None + assert index.getItemHeader("", "") is None + assert index.getItemHeader(C.hNovelRoot, "") is None - assert theIndex.scanText(cHandle, ( + assert index.scanText(cHandle, ( "# Jane Smith\n" "@tag: Jane\n" )) - assert theIndex.scanText(nHandle, ( + assert index.scanText(nHandle, ( "# Hello World!\n" "@pov: Jane\n" "@char: Jane\n\n" @@ -537,7 +553,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): # The novel structure should contain the pointer to the novel file header theKeys = [] - for aKey, _, _, _ in theIndex.novelStructure(): + for aKey, _, _, _ in index.novelStructure(): theKeys.append(aKey) assert theKeys == [ @@ -548,10 +564,10 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): ] # Check that excluded files can be skipped - theProject.tree[nHandle].setActive(False) + project.tree[nHandle].setActive(False) # type: ignore theKeys = [] - for aKey, _, _, _ in theIndex.novelStructure(skipExcl=False): + for aKey, _, _, _ in index.novelStructure(skipExcl=False): theKeys.append(aKey) assert theKeys == [ @@ -562,7 +578,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): ] theKeys = [] - for aKey, _, _, _ in theIndex.novelStructure(skipExcl=True): + for aKey, _, _, _ in index.novelStructure(skipExcl=True): theKeys.append(aKey) assert theKeys == [ @@ -572,7 +588,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): ] # The novel file should have the correct counts - cC, wC, pC = theIndex.getCounts(nHandle) + cC, wC, pC = index.getCounts(nHandle) assert cC == 62 # Characters in text and title only assert wC == 12 # Words in text and title only assert pC == 2 # Paragraphs in text only @@ -580,21 +596,21 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): # getItemData + getHandleHeaderCount # ================================== - theItem = theIndex.getItemData(nHandle) + theItem = index.getItemData(nHandle) assert isinstance(theItem, IndexItem) assert theItem.headings() == ["T0001"] - assert theIndex.getHandleHeaderCount(nHandle) == 1 + assert index.getHandleHeaderCount(nHandle) == 1 # getReferences # ============= # Look up an invalid handle - theRefs = theIndex.getReferences("Not a handle") + theRefs = index.getReferences("Not a handle") assert theRefs["@pov"] == [] assert theRefs["@char"] == [] # The novel file should now refer to Jane as @pov and @char - theRefs = theIndex.getReferences(nHandle) + theRefs = index.getReferences(nHandle) assert theRefs["@pov"] == ["Jane"] assert theRefs["@char"] == ["Jane"] @@ -602,31 +618,31 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): # ==================== # None handle should return an empty dict - assert theIndex.getBackReferenceList(None) == {} + assert index.getBackReferenceList(None) == {} # type: ignore # The Title Page file should have no references as it has no tag - assert theIndex.getBackReferenceList(C.hTitlePage) == {} + assert index.getBackReferenceList(C.hTitlePage) == {} # The character file should have a record of the reference from the novel file - theRefs = theIndex.getBackReferenceList(cHandle) + theRefs = index.getBackReferenceList(cHandle) assert theRefs == {nHandle: "T0001"} # getTagSource # ============ - assert theIndex.getTagSource("Jane") == (cHandle, "T0001") - assert theIndex.getTagSource("John") == (None, "T0000") + assert index.getTagSource("Jane") == (cHandle, "T0001") + assert index.getTagSource("John") == (None, "T0000") # getCounts # ========= # For whole text and sections # Invalid handle or title should return 0s - assert theIndex.getCounts("stuff") == (0, 0, 0) - assert theIndex.getCounts(nHandle, "stuff") == (0, 0, 0) + assert index.getCounts("stuff") == (0, 0, 0) + assert index.getCounts(nHandle, "stuff") == (0, 0, 0) # Get section counts for a novel file - assert theIndex.scanText(nHandle, ( + assert index.scanText(nHandle, ( "# Hello World!\n" "@pov: Jane\n" "@char: Jane\n\n" @@ -641,25 +657,25 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): "Well, not really. She's still awesome though.\n" )) # Whole document - cC, wC, pC = theIndex.getCounts(nHandle) + cC, wC, pC = index.getCounts(nHandle) assert cC == 152 assert wC == 28 assert pC == 4 # First part - cC, wC, pC = theIndex.getCounts(nHandle, "T0001") + cC, wC, pC = index.getCounts(nHandle, "T0001") assert cC == 62 assert wC == 12 assert pC == 2 # Second part - cC, wC, pC = theIndex.getCounts(nHandle, "T0002") + cC, wC, pC = index.getCounts(nHandle, "T0002") assert cC == 90 assert wC == 16 assert pC == 2 # Get section counts for a note file - assert theIndex.scanText(cHandle, ( + assert index.scanText(cHandle, ( "# Hello World!\n" "@pov: Jane\n" "@char: Jane\n\n" @@ -674,19 +690,19 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): "Well, not really. She's still awesome though.\n" )) # Whole document - cC, wC, pC = theIndex.getCounts(cHandle) + cC, wC, pC = index.getCounts(cHandle) assert cC == 152 assert wC == 28 assert pC == 4 # First part - cC, wC, pC = theIndex.getCounts(cHandle, "T0001") + cC, wC, pC = index.getCounts(cHandle, "T0001") assert cC == 62 assert wC == 12 assert pC == 2 # Second part - cC, wC, pC = theIndex.getCounts(cHandle, "T0002") + cC, wC, pC = index.getCounts(cHandle, "T0002") assert cC == 90 assert wC == 16 assert pC == 2 @@ -694,19 +710,19 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): # Novel Stats # =========== - hHandle = theProject.newFile("Chapter", C.hNovelRoot) - sHandle = theProject.newFile("Scene One", C.hNovelRoot) - tHandle = theProject.newFile("Scene Two", C.hNovelRoot) + hHandle = project.newFile("Chapter", C.hNovelRoot) + sHandle = project.newFile("Scene One", C.hNovelRoot) + tHandle = project.newFile("Scene Two", C.hNovelRoot) - theProject.tree[hHandle].itemLayout == nwItemLayout.DOCUMENT - theProject.tree[sHandle].itemLayout == nwItemLayout.DOCUMENT - theProject.tree[tHandle].itemLayout == nwItemLayout.DOCUMENT + project.tree[hHandle].itemLayout == nwItemLayout.DOCUMENT # type: ignore + project.tree[sHandle].itemLayout == nwItemLayout.DOCUMENT # type: ignore + project.tree[tHandle].itemLayout == nwItemLayout.DOCUMENT # type: ignore - assert theIndex.scanText(hHandle, "## Chapter One\n\n") - assert theIndex.scanText(sHandle, "### Scene One\n\n") - assert theIndex.scanText(tHandle, "### Scene Two\n\n") + assert index.scanText(hHandle, "## Chapter One\n\n") # type: ignore + assert index.scanText(sHandle, "### Scene One\n\n") # type: ignore + assert index.scanText(tHandle, "### Scene Two\n\n") # type: ignore - assert [(h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False)] == [ + assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(skipExcl=False)] == [ (C.hTitlePage, "T0001"), (C.hChapterDoc, "T0001"), (C.hSceneDoc, "T0001"), @@ -717,7 +733,7 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): (tHandle, "T0001"), ] - assert [(h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=True)] == [ + assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(skipExcl=True)] == [ (C.hTitlePage, "T0001"), (C.hChapterDoc, "T0001"), (C.hSceneDoc, "T0001"), @@ -727,8 +743,8 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): ] # Add a fake handle to the tree and check that it's ignored - theProject.tree._order.append("0000000000000") - assert [(h, t) for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False)] == [ + project.tree._order.append("0000000000000") + assert [(h, t) for h, t, _ in index._itemIndex.iterNovelStructure(skipExcl=False)] == [ (C.hTitlePage, "T0001"), (C.hChapterDoc, "T0001"), (C.hSceneDoc, "T0001"), @@ -738,25 +754,25 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): (sHandle, "T0001"), (tHandle, "T0001"), ] - theProject.tree._order.remove("0000000000000") + project.tree._order.remove("0000000000000") # Extract stats - assert theIndex.getNovelWordCount(skipExcl=False) == 43 - assert theIndex.getNovelWordCount(skipExcl=True) == 15 - assert theIndex.getNovelTitleCounts(skipExcl=False) == [0, 3, 2, 3, 0] - assert theIndex.getNovelTitleCounts(skipExcl=True) == [0, 1, 2, 3, 0] + assert index.getNovelWordCount(skipExcl=False) == 43 + assert index.getNovelWordCount(skipExcl=True) == 15 + assert index.getNovelTitleCounts(skipExcl=False) == [0, 3, 2, 3, 0] + assert index.getNovelTitleCounts(skipExcl=True) == [0, 1, 2, 3, 0] # Table of Contents - assert theIndex.getTableOfContents(C.hNovelRoot, 0, skipExcl=True) == [] - assert theIndex.getTableOfContents(C.hNovelRoot, 1, skipExcl=True) == [ + assert index.getTableOfContents(C.hNovelRoot, 0, skipExcl=True) == [] + assert index.getTableOfContents(C.hNovelRoot, 1, skipExcl=True) == [ (f"{C.hTitlePage}:T0001", 1, "New Novel", 15), ] - assert theIndex.getTableOfContents(C.hNovelRoot, 2, skipExcl=True) == [ + assert index.getTableOfContents(C.hNovelRoot, 2, skipExcl=True) == [ (f"{C.hTitlePage}:T0001", 1, "New Novel", 5), (f"{C.hChapterDoc}:T0001", 2, "New Chapter", 4), (f"{hHandle}:T0001", 2, "Chapter One", 6), ] - assert theIndex.getTableOfContents(C.hNovelRoot, 3, skipExcl=True) == [ + assert index.getTableOfContents(C.hNovelRoot, 3, skipExcl=True) == [ (f"{C.hTitlePage}:T0001", 1, "New Novel", 5), (f"{C.hChapterDoc}:T0001", 2, "New Chapter", 2), (f"{C.hSceneDoc}:T0001", 3, "New Scene", 2), @@ -765,16 +781,16 @@ def testCoreIndex_ExtractData(mockGUI, fncPath, mockRnd): (f"{tHandle}:T0001", 3, "Scene Two", 2), ] - assert theIndex.getTableOfContents(C.hNovelRoot, 0, skipExcl=False) == [] - assert theIndex.getTableOfContents(C.hNovelRoot, 1, skipExcl=False) == [ + assert index.getTableOfContents(C.hNovelRoot, 0, skipExcl=False) == [] + assert index.getTableOfContents(C.hNovelRoot, 1, skipExcl=False) == [ (f"{C.hTitlePage}:T0001", 1, "New Novel", 9), (f"{nHandle}:T0001", 1, "Hello World!", 12), (f"{nHandle}:T0002", 1, "Hello World!", 22), ] - assert theIndex.saveIndex() is True - assert theProject.saveProject() is True - theProject.closeProject() + assert index.saveIndex() is True + assert project.saveProject() is True + project.closeProject() # END Test testCoreIndex_ExtractData @@ -787,17 +803,20 @@ def testCoreIndex_TagsIndex(): # Expected data content = { - "Tag1": { + "tag1": { + "name": "Tag1", "handle": "0000000000001", "heading": "T0001", "class": nwItemClass.NOVEL.name, }, - "Tag2": { + "tag2": { + "name": "Tag2", "handle": "0000000000002", "heading": "T0002", "class": nwItemClass.CHARACTER.name, }, - "Tag3": { + "tag3": { + "name": "Tag3", "handle": "0000000000003", "heading": "T0003", "class": nwItemClass.PLOT.name, @@ -811,9 +830,9 @@ def testCoreIndex_TagsIndex(): assert tagsIndex._tags == content # Get items - assert tagsIndex["Tag1"] == content["Tag1"] - assert tagsIndex["Tag2"] == content["Tag2"] - assert tagsIndex["Tag3"] == content["Tag3"] + assert tagsIndex["Tag1"] == content["tag1"] + assert tagsIndex["Tag2"] == content["tag2"] + assert tagsIndex["Tag3"] == content["tag3"] assert tagsIndex["Tag4"] is None # Contains @@ -866,12 +885,23 @@ def testCoreIndex_TagsIndex(): # Invalid data type with pytest.raises(ValueError): - tagsIndex.unpackData([]) + tagsIndex.unpackData([]) # type: ignore # Invalid key with pytest.raises(ValueError): tagsIndex.unpackData({ 1234: { + "name": "1234", + "handle": "0000000000001", + "heading": "T0001", + "class": "NOVEL", + } + }) + + # Missing name + with pytest.raises(KeyError): + tagsIndex.unpackData({ + "tag1": { "handle": "0000000000001", "heading": "T0001", "class": "NOVEL", @@ -881,7 +911,8 @@ def testCoreIndex_TagsIndex(): # Missing handle with pytest.raises(KeyError): tagsIndex.unpackData({ - "Tag1": { + "tag1": { + "name": "Tag1", "heading": "T0001", "class": "NOVEL", } @@ -890,7 +921,8 @@ def testCoreIndex_TagsIndex(): # Missing heading with pytest.raises(KeyError): tagsIndex.unpackData({ - "Tag1": { + "tag1": { + "name": "Tag1", "handle": "0000000000001", "class": "NOVEL", } @@ -899,16 +931,29 @@ def testCoreIndex_TagsIndex(): # Missing class with pytest.raises(KeyError): tagsIndex.unpackData({ - "Tag1": { + "tag1": { + "name": "Tag1", "handle": "0000000000001", "heading": "T0001", } }) + # Invalid key case + with pytest.raises(ValueError): + tagsIndex.unpackData({ + "Tag1": { + "name": "Tag1", + "handle": "blablabla", + "heading": "T0001", + "class": "NOVEL", + } + }) + # Invalid handle with pytest.raises(ValueError): tagsIndex.unpackData({ - "Tag1": { + "tag1": { + "name": "Tag1", "handle": "blablabla", "heading": "T0001", "class": "NOVEL", @@ -918,7 +963,8 @@ def testCoreIndex_TagsIndex(): # Invalid heading with pytest.raises(ValueError): tagsIndex.unpackData({ - "Tag1": { + "tag1": { + "name": "Tag1", "handle": "0000000000001", "heading": "blabla", "class": "NOVEL", @@ -928,7 +974,8 @@ def testCoreIndex_TagsIndex(): # Invalid class with pytest.raises(ValueError): tagsIndex.unpackData({ - "Tag1": { + "tag1": { + "name": "Tag1", "handle": "0000000000001", "heading": "T0001", "class": "blabla", @@ -941,17 +988,17 @@ def testCoreIndex_TagsIndex(): @pytest.mark.core def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): """Check the ItemIndex class.""" - theProject = NWProject() + project = NWProject() mockRnd.reset() - buildTestProject(theProject, fncPath) - theProject.index.clearIndex() + buildTestProject(project, fncPath) + project.index.clearIndex() nHandle = C.hTitlePage cHandle = C.hChapterDoc sHandle = C.hSceneDoc - assert theProject.index.saveIndex() is True - itemIndex = theProject.index._itemIndex + assert project.index.saveIndex() is True + itemIndex = project.index._itemIndex # The index should be empty assert nHandle not in itemIndex @@ -963,9 +1010,9 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): assert cHandle not in itemIndex # Add the novel chapter file - itemIndex.add(cHandle, theProject.tree[cHandle]) + itemIndex.add(cHandle, project.tree[cHandle]) # type: ignore assert cHandle in itemIndex - assert itemIndex[cHandle].item == theProject.tree[cHandle] + assert itemIndex[cHandle].item == project.tree[cHandle] # type: ignore assert itemIndex.allItemTags(cHandle) == [] assert list(itemIndex.iterItemHeaders(cHandle))[0][0] == "T0000" @@ -986,17 +1033,17 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): idxData = itemIndex.packData() assert idxData[cHandle]["headings"]["T0001"] == { - "level": "H2", "line": 1, "title": "Chapter One", "tag": "One", + "level": "H2", "line": 1, "title": "Chapter One", "tag": "one", "cCount": 60, "wCount": 10, "pCount": 2, "synopsis": "In the beginning ...", } - assert "@pov" in idxData[cHandle]["references"]["T0001"]["Jane"] - assert "@focus" in idxData[cHandle]["references"]["T0001"]["Jane"] - assert "@char" in idxData[cHandle]["references"]["T0001"]["Jane"] - assert "@char" in idxData[cHandle]["references"]["T0001"]["John"] + assert "@pov" in idxData[cHandle]["references"]["T0001"]["jane"] + assert "@focus" in idxData[cHandle]["references"]["T0001"]["jane"] + assert "@char" in idxData[cHandle]["references"]["T0001"]["jane"] + assert "@char" in idxData[cHandle]["references"]["T0001"]["john"] # Add the other two files - itemIndex.add(nHandle, theProject.tree[nHandle]) - itemIndex.add(sHandle, theProject.tree[sHandle]) + itemIndex.add(nHandle, project.tree[nHandle]) # type: ignore + itemIndex.add(sHandle, project.tree[sHandle]) # type: ignore itemIndex.addItemHeading(nHandle, 1, "H1", "Novel") itemIndex.addItemHeading(sHandle, 1, "H3", "Scene One") @@ -1005,32 +1052,32 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): # Check repr strings assert repr(itemIndex[nHandle]) == f"" - assert repr(itemIndex[nHandle]["T0001"]) == "" + assert repr(itemIndex[nHandle]["T0001"]) == "" # type: ignore # Check content of a single item - assert "T0001" in itemIndex[nHandle] - assert itemIndex[cHandle].allTags() == ["One"] + assert "T0001" in itemIndex[nHandle] # type: ignore + assert itemIndex[cHandle].allTags() == ["one"] # type: ignore # Check the content of a single heading - assert itemIndex[cHandle]["T0001"].key == "T0001" - assert itemIndex[cHandle]["T0001"].level == "H2" - assert itemIndex[cHandle]["T0001"].line == 1 - assert itemIndex[cHandle]["T0001"].title == "Chapter One" - assert itemIndex[cHandle]["T0001"].tag == "One" - assert itemIndex[cHandle]["T0001"].charCount == 60 - assert itemIndex[cHandle]["T0001"].wordCount == 10 - assert itemIndex[cHandle]["T0001"].paraCount == 2 - assert itemIndex[cHandle]["T0001"].synopsis == "In the beginning ..." - assert "Jane" in itemIndex[cHandle]["T0001"].references - assert "John" in itemIndex[cHandle]["T0001"].references + assert itemIndex[cHandle]["T0001"].key == "T0001" # type: ignore + assert itemIndex[cHandle]["T0001"].level == "H2" # type: ignore + assert itemIndex[cHandle]["T0001"].line == 1 # type: ignore + assert itemIndex[cHandle]["T0001"].title == "Chapter One" # type: ignore + assert itemIndex[cHandle]["T0001"].tag == "one" # type: ignore + assert itemIndex[cHandle]["T0001"].charCount == 60 # type: ignore + assert itemIndex[cHandle]["T0001"].wordCount == 10 # type: ignore + assert itemIndex[cHandle]["T0001"].paraCount == 2 # type: ignore + assert itemIndex[cHandle]["T0001"].synopsis == "In the beginning ..." # type: ignore + assert "jane" in itemIndex[cHandle]["T0001"].references # type: ignore + assert "john" in itemIndex[cHandle]["T0001"].references # type: ignore # Check heading level setter - itemIndex[cHandle]["T0001"].setLevel("H3") # Change it - assert itemIndex[cHandle]["T0001"].level == "H3" - itemIndex[cHandle]["T0001"].setLevel("H2") # Set it back - assert itemIndex[cHandle]["T0001"].level == "H2" - itemIndex[cHandle]["T0001"].setLevel("H5") # Invalid level - assert itemIndex[cHandle]["T0001"].level == "H2" + itemIndex[cHandle]["T0001"].setLevel("H3") # Change it # type: ignore + assert itemIndex[cHandle]["T0001"].level == "H3" # type: ignore + itemIndex[cHandle]["T0001"].setLevel("H2") # Set it back # type: ignore + assert itemIndex[cHandle]["T0001"].level == "H2" # type: ignore + itemIndex[cHandle]["T0001"].setLevel("H5") # Invalid level # type: ignore + assert itemIndex[cHandle]["T0001"].level == "H2" # type: ignore # Data Extraction # =============== @@ -1051,10 +1098,10 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): # =============== # Add a second novel - mHandle = theProject.newRoot(nwItemClass.NOVEL) - uHandle = theProject.newFile("Title Page", mHandle) - itemIndex.add(uHandle, theProject.tree[uHandle]) - itemIndex.addItemHeading(uHandle, "T0001", "H1", "Novel 2") + mHandle = project.newRoot(nwItemClass.NOVEL) + uHandle = project.newFile("Title Page", mHandle) + itemIndex.add(uHandle, project.tree[uHandle]) # type: ignore + itemIndex.addItemHeading(uHandle, "T0001", "H1", "Novel 2") # type: ignore assert uHandle in itemIndex # Structure of all novels @@ -1077,7 +1124,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): assert nStruct[0][0] == uHandle # Inject garbage into tree - theProject.tree._order.append("stuff") + project.tree._order.append("stuff") nStruct = list(itemIndex.iterNovelStructure()) assert len(nStruct) == 4 assert nStruct[0][0] == nHandle @@ -1086,7 +1133,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): assert nStruct[3][0] == uHandle # Skip excluded - theProject.tree[sHandle].setActive(False) + project.tree[sHandle].setActive(False) # type: ignore nStruct = list(itemIndex.iterNovelStructure(skipExcl=True)) assert len(nStruct) == 3 assert nStruct[0][0] == nHandle @@ -1094,7 +1141,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): assert nStruct[2][0] == uHandle # Delete new item - del itemIndex[uHandle] + del itemIndex[uHandle] # type: ignore assert uHandle not in itemIndex # Unpack Error Handling @@ -1109,7 +1156,7 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): # Data must be dictionary with pytest.raises(ValueError): - itemIndex.unpackData("stuff") + itemIndex.unpackData("stuff") # type: ignore # Keys must be valid handles with pytest.raises(ValueError): @@ -1134,8 +1181,8 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): "references": {"T0001": {}, "T0002": {}}, } }) - assert "T0001" in itemIndex[cHandle] - assert "T0002" not in itemIndex[cHandle] + assert "T0001" in itemIndex[cHandle] # type: ignore + assert "T0002" not in itemIndex[cHandle] # type: ignore itemIndex.clear() # Tag keys must be strings @@ -1180,8 +1227,8 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): def testCoreIndex_CountWords(): """Test the word counter and the exclusion filers.""" # Non-Text - assert countWords(None) == (0, 0, 0) - assert countWords(1234) == (0, 0, 0) + assert countWords(None) == (0, 0, 0) # type: ignore + assert countWords(1234) == (0, 0, 0) # type: ignore # General Text cC, wC, pC = countWords((