Add document duplication tool and support in item and tree classes

This commit is contained in:
Veronica Berglyd Olsen
2023-07-20 18:51:26 +02:00
parent 1c5466a799
commit 567045e4ff
3 changed files with 86 additions and 9 deletions
+45 -2
View File
@@ -40,7 +40,7 @@ from novelwriter.constants import nwItemClass
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
if TYPE_CHECKING:
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
logger = logging.getLogger(__name__)
@@ -76,7 +76,7 @@ class DocMerger:
return
def newTargetDoc(self, srcHandle: str, docLabel: str) -> str | None:
"""Create a barnd new target document based on a source handle
"""Create a brand new target document based on a source handle
and a new doc label. Calling this function resets the class.
"""
srcItem = self._project.tree[srcHandle]
@@ -263,6 +263,49 @@ class DocSplitter:
# END Class DocSplitter
class DocDuplicator:
"""A class that will duplicate all documents and folders starting
from a given handle.
"""
def __init__(self, project: NWProject) -> None:
self._project = project
return
##
# Methods
##
def duplicate(self, items: list[str]) -> Iterable[tuple[str, str | None]]:
"""Run through a list of items, duplicate them, and copy the
text content if they are documents.
"""
if not items:
return
nHandle = items[0]
hMap: dict[str, str | None] = {t: None for t in items}
for tHandle in items:
newItem = self._project.tree.duplicate(tHandle)
if newItem is None or newItem.itemHandle is None:
return
hMap[tHandle] = newItem.itemHandle
if newItem.itemParent in hMap:
newItem.setParent(hMap[newItem.itemParent])
if newItem.isFileType():
oldDoc = self._project.storage.getDocument(tHandle)
newDoc = self._project.storage.getDocument(newItem.itemHandle)
if newDoc.fileExists():
return
newDoc.writeDocument(oldDoc.readDocument() or "")
yield newItem.itemHandle, nHandle
nHandle = None
return
# END Class DocDuplicator
class ProjectBuilder:
"""A class to build a new project from a set of user-defined
parameter provided by the New Projecty Wizard.
+25 -2
View File
@@ -83,6 +83,29 @@ class NWItem:
def __bool__(self) -> bool:
return self._handle is not None
def __copy__(self) -> NWItem:
"""Make a shallow copy of the current item."""
item = NWItem(self._project)
item._name = self._name
item._handle = self._handle
item._parent = self._parent
item._root = self._root
item._order = self._order
item._type = self._type
item._class = self._class
item._layout = self._layout
item._status = self._status
item._import = self._import
item._active = self._active
item._expanded = self._expanded
item._heading = self._heading
item._charCount = self._charCount
item._wordCount = self._wordCount
item._paraCount = self._paraCount
item._cursorPos = self._cursorPos
item._initCount = self._initCount
return item
##
# Properties
##
@@ -163,7 +186,7 @@ class NWItem:
# Pack/Unpack Data
##
def pack(self) -> dict[str, dict[str, str]]:
def pack(self) -> dict:
"""Pack all the data in the class instance into a dictionary."""
item: dict[str, str] = {}
meta: dict[str, str] = {}
@@ -197,7 +220,7 @@ class NWItem:
return data
def unpack(self, data: dict[str, dict[str, Any]]) -> bool:
def unpack(self, data: dict) -> bool:
"""Set the values from a data dictionary."""
item = data.get("itemAttr", {})
meta = data.get("metaAttr", {})
+16 -5
View File
@@ -24,10 +24,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import copy
import random
import logging
from typing import TYPE_CHECKING, Any, Iterator
from typing import TYPE_CHECKING, Iterator
from pathlib import Path
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType
@@ -114,7 +115,17 @@ class NWTree:
return True
def pack(self) -> list[dict[str, dict[str, str]]]:
def duplicate(self, sHandle: str) -> NWItem | None:
"""Duplicate an item and set a new handle."""
sItem = self.__getitem__(sHandle)
if isinstance(sItem, NWItem):
nItem = copy.copy(sItem)
if self.append(None, sItem.itemParent, nItem):
logger.info("Duplicated item '%s' -> '%s'", sHandle, nItem.itemHandle)
return nItem
return None
def pack(self) -> list[dict]:
"""Pack the content of the tree into a list of doctionaries of
items. In the order defined by the _treeOrder list.
"""
@@ -125,7 +136,7 @@ class NWTree:
tree.append(tItem.pack())
return tree
def unpack(self, data: list[dict[str, dict[str, Any]]]) -> None:
def unpack(self, data: list[dict]) -> None:
"""Iterate through all items of a list and add them to the
project tree.
"""
@@ -348,11 +359,11 @@ class NWTree:
"""True if there are any items in the project."""
return bool(self._treeOrder)
def __getitem__(self, tHandle: str) -> NWItem | None:
def __getitem__(self, tHandle: str | None) -> NWItem | None:
"""Return a project item based on its handle. Returns None if
the handle doesn't exist in the project.
"""
if tHandle in self._projTree:
if tHandle and tHandle in self._projTree:
return self._projTree[tHandle]
logger.error("No tree item with handle '%s'", str(tHandle))
return None