Move the tags index into a wrapper class
This commit is contained in:
+95
-26
@@ -56,7 +56,7 @@ class NWIndex():
|
||||
self._indexBroken = False
|
||||
|
||||
# Indices
|
||||
self._tags = {}
|
||||
self._tagsIndex = TagsIndex()
|
||||
self._itemIndex = ItemIndex(theProject)
|
||||
|
||||
# TimeStamps
|
||||
@@ -81,7 +81,7 @@ class NWIndex():
|
||||
def clearIndex(self):
|
||||
"""Clear the index dictionaries and time stamps.
|
||||
"""
|
||||
self._tags = {}
|
||||
self._tagsIndex.clear()
|
||||
self._itemIndex.clear()
|
||||
self._timeNovel = 0
|
||||
self._timeNotes = 0
|
||||
@@ -93,7 +93,7 @@ class NWIndex():
|
||||
"""
|
||||
logger.debug("Removing item '%s' from the index", tHandle)
|
||||
for tTag in self._itemIndex.allItemTags(tHandle):
|
||||
self._tags.pop(tTag, None)
|
||||
del self._tagsIndex[tTag]
|
||||
|
||||
del self._itemIndex[tHandle]
|
||||
|
||||
@@ -152,7 +152,7 @@ class NWIndex():
|
||||
return False
|
||||
|
||||
try:
|
||||
self._validateTagsIndex(theData["tagsIndex"])
|
||||
self._tagsIndex.unpackData(theData["tagsIndex"])
|
||||
self._itemIndex.unpackData(theData["itemIndex"])
|
||||
except Exception:
|
||||
logger.error("The index content is invalid")
|
||||
@@ -186,10 +186,11 @@ class NWIndex():
|
||||
tStart = time()
|
||||
|
||||
try:
|
||||
tagsIndex = self._tagsIndex.packData()
|
||||
itemIndex = self._itemIndex.packData()
|
||||
with open(indexFile, mode="w+", encoding="utf-8") as outFile:
|
||||
outFile.write("{\n")
|
||||
outFile.write(f' "tagsIndex": {jsonEncode(self._tags, n=1, nmax=2)},\n')
|
||||
outFile.write(f' "tagsIndex": {jsonEncode(tagsIndex, n=1, nmax=2)},\n')
|
||||
outFile.write(f' "itemIndex": {jsonEncode(itemIndex, n=1, nmax=4)}\n')
|
||||
outFile.write("}\n")
|
||||
|
||||
@@ -356,11 +357,7 @@ class NWIndex():
|
||||
|
||||
sTitle = f"T{nTitle:06d}"
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
self._tags[theBits[1]] = {
|
||||
"handle": tHandle,
|
||||
"heading": sTitle,
|
||||
"class": itemClass.name,
|
||||
}
|
||||
self._tagsIndex.add(theBits[1], tHandle, sTitle, itemClass)
|
||||
self._itemIndex.setHeadingTag(tHandle, sTitle, theBits[1])
|
||||
else:
|
||||
self._itemIndex.addHeadingReferences(tHandle, sTitle, theBits[1:], theBits[0])
|
||||
@@ -425,8 +422,8 @@ class NWIndex():
|
||||
|
||||
# For a tag, only the first value is accepted, the rest are ignored
|
||||
if theBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
|
||||
if theBits[1] in self._tags:
|
||||
isGood[1] = self._tags[theBits[1]].get("handle") == tItem.itemHandle
|
||||
if theBits[1] in self._tagsIndex:
|
||||
isGood[1] = self._tagsIndex.tagHandle(theBits[1]) == tItem.itemHandle
|
||||
else:
|
||||
isGood[1] = True
|
||||
return isGood
|
||||
@@ -434,8 +431,8 @@ class NWIndex():
|
||||
# If we're still here, we check that the references exist
|
||||
theKey = nwKeyWords.KEY_CLASS[theBits[0]].name
|
||||
for n in range(1, nBits):
|
||||
if theBits[n] in self._tags:
|
||||
isGood[n] = theKey == self._tags[theBits[n]].get("class")
|
||||
if theBits[n] in self._tagsIndex:
|
||||
isGood[n] = self._tagsIndex.tagClass(theBits[n]) == theKey
|
||||
|
||||
return isGood
|
||||
|
||||
@@ -582,22 +579,98 @@ class NWIndex():
|
||||
def getTagSource(self, theTag):
|
||||
"""Return the source location of a given tag.
|
||||
"""
|
||||
ref = self._tags.get(theTag, {})
|
||||
return ref.get("handle"), ref.get("heading", H_NONE)
|
||||
tHandle = self._tagsIndex.tagHandle(theTag)
|
||||
sTitle = self._tagsIndex.tagHeading(theTag)
|
||||
return tHandle, sTitle
|
||||
|
||||
# END Class NWIndex
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Indexer Objects
|
||||
# =============================================================================================== #
|
||||
|
||||
class TagsIndex:
|
||||
"""A wrapper class that holds the reverse lookup tags index.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._tags = {}
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
# Methods
|
||||
##
|
||||
|
||||
def _validateTagsIndex(self, tagsIndex):
|
||||
def clear(self):
|
||||
"""Clear the index.
|
||||
"""
|
||||
self._tags = {}
|
||||
return
|
||||
|
||||
def __contains__(self, tagKey):
|
||||
"""Check if a tag exists in the index,
|
||||
"""
|
||||
return tagKey in self._tags
|
||||
|
||||
def __delitem__(self, tagKey):
|
||||
"""Delete an entry in the index.
|
||||
"""
|
||||
self._tags.pop(tagKey, None)
|
||||
return
|
||||
|
||||
def __getitem__(self, tagKey):
|
||||
"""Return a tag, or return None if it isn't found.
|
||||
"""
|
||||
return self._tags.get(tagKey, None)
|
||||
|
||||
def add(self, tagKey, tHandle, sTitle, itemClass):
|
||||
"""Add a key to the index and set all values.
|
||||
"""
|
||||
self._tags[tagKey] = {
|
||||
"handle": tHandle, "heading": sTitle, "class": itemClass.name
|
||||
}
|
||||
return
|
||||
|
||||
def tagHandle(self, tagKey):
|
||||
"""Get the handle of a given tag.
|
||||
"""
|
||||
if tagKey in self._tags:
|
||||
return self._tags.get(tagKey).get("handle")
|
||||
return None
|
||||
|
||||
def tagHeading(self, tagKey):
|
||||
"""Get the heading of a given tag.
|
||||
"""
|
||||
if tagKey in self._tags:
|
||||
return self._tags.get(tagKey).get("heading")
|
||||
return H_NONE
|
||||
|
||||
def tagClass(self, tagKey):
|
||||
"""Get the class of a given tag.
|
||||
"""
|
||||
if tagKey in self._tags:
|
||||
return self._tags.get(tagKey).get("class")
|
||||
return None
|
||||
|
||||
##
|
||||
# Pack/Unpack
|
||||
##
|
||||
|
||||
def packData(self):
|
||||
"""Pack all the data of the tags into a single dictionary.
|
||||
"""
|
||||
return self._tags
|
||||
|
||||
def unpackData(self, data):
|
||||
"""Iterate through the tagsIndex loaded from cache and check
|
||||
that it's valid.
|
||||
"""
|
||||
self._tags = {}
|
||||
if not isinstance(tagsIndex, dict):
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("tagsIndex is not a dict")
|
||||
|
||||
for tagKey, tagData in tagsIndex.items():
|
||||
for tagKey, tagData in data.items():
|
||||
if not isinstance(tagKey, str):
|
||||
raise ValueError("tagsIndex keys must be a strings")
|
||||
if "handle" not in tagData:
|
||||
@@ -613,17 +686,13 @@ class NWIndex():
|
||||
if not isItemClass(tagData["class"]):
|
||||
raise ValueError("tagsIndex handle must be an nwItemClass")
|
||||
|
||||
self._tags = tagsIndex
|
||||
self._tags = data
|
||||
|
||||
return
|
||||
|
||||
# END Class NWIndex
|
||||
# END Class TagsIndex
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Indexer Objects
|
||||
# =============================================================================================== #
|
||||
|
||||
class ItemIndex:
|
||||
"""A wrapper object holding the indexed items.
|
||||
"""
|
||||
|
||||
@@ -68,19 +68,19 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
assert theIndex.saveIndex() is True
|
||||
|
||||
# Take a copy of the index
|
||||
tagIndex = str(theIndex._tags)
|
||||
tagIndex = str(theIndex._tagsIndex.packData())
|
||||
itemsIndex = str(theIndex._itemIndex.packData())
|
||||
|
||||
# Delete a handle
|
||||
assert theIndex._tags.get("Bod", None) is not None
|
||||
assert theIndex._tagsIndex["Bod"] is not None
|
||||
assert theIndex._itemIndex["4c4f28287af27"] is not None
|
||||
theIndex.deleteHandle("4c4f28287af27")
|
||||
assert theIndex._tags.get("Bod", None) is None
|
||||
assert theIndex._tagsIndex["Bod"] is None
|
||||
assert theIndex._itemIndex["4c4f28287af27"] is None
|
||||
|
||||
# Clear the index
|
||||
theIndex.clearIndex()
|
||||
assert theIndex._tags == {}
|
||||
assert theIndex._tagsIndex._tags == {}
|
||||
assert theIndex._itemIndex._items == {}
|
||||
|
||||
# Make the load fail
|
||||
@@ -91,7 +91,7 @@ def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
|
||||
# Make the load pass
|
||||
assert theIndex.loadIndex() is True
|
||||
|
||||
assert str(theIndex._tags) == tagIndex
|
||||
assert str(theIndex._tagsIndex.packData()) == tagIndex
|
||||
assert str(theIndex._itemIndex.packData()) == itemsIndex
|
||||
|
||||
# Break the index and check that we notice
|
||||
@@ -188,9 +188,9 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI):
|
||||
"@pov: Jane\n"
|
||||
"@invalid: John\n" # Checks for issue #688
|
||||
))
|
||||
assert theIndex._tags == {
|
||||
"Jane": {"handle": cHandle, "heading": "T000001", "class": "CHARACTER"}
|
||||
}
|
||||
assert theIndex._tagsIndex.tagHandle("Jane") == cHandle
|
||||
assert theIndex._tagsIndex.tagHeading("Jane") == "T000001"
|
||||
assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER"
|
||||
assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!"
|
||||
assert theIndex.getReferences(nHandle, "T000001") == {
|
||||
"@char": [],
|
||||
@@ -301,9 +301,9 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
))
|
||||
assert theIndex._tags == {
|
||||
"Jane": {"handle": cHandle, "heading": "T000001", "class": "CHARACTER"}
|
||||
}
|
||||
assert theIndex._tagsIndex.tagHandle("Jane") == cHandle
|
||||
assert theIndex._tagsIndex.tagHeading("Jane") == "T000001"
|
||||
assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER"
|
||||
assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!"
|
||||
|
||||
# Title Indexing
|
||||
|
||||
@@ -47,7 +47,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
|
||||
# Rebuild the index
|
||||
nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger)
|
||||
assert nwGUI.theProject.index._tags != {}
|
||||
assert nwGUI.theProject.index._tagsIndex._tags != {}
|
||||
assert nwGUI.theProject.index._itemIndex._items != {}
|
||||
|
||||
# Select a document in the project tree
|
||||
|
||||
Reference in New Issue
Block a user