Move new project tool to core tools (resolves #1152)
This commit is contained in:
@@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from novelwriter.core.coretools import DocMerger, DocSplitter
|
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
from novelwriter.core.index import countWords
|
from novelwriter.core.index import countWords
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
@@ -31,6 +31,7 @@ from novelwriter.core.tomd import ToMarkdown
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
"DocMerger",
|
"DocMerger",
|
||||||
"DocSplitter",
|
"DocSplitter",
|
||||||
|
"ProjectBuilder",
|
||||||
"countWords",
|
"countWords",
|
||||||
"NWDoc",
|
"NWDoc",
|
||||||
"NWProject",
|
"NWProject",
|
||||||
|
|||||||
@@ -24,15 +24,30 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
|
import novelwriter
|
||||||
|
|
||||||
from novelwriter.common import minmax
|
from time import time
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QCoreApplication
|
||||||
|
|
||||||
|
from novelwriter.enum import nwAlert
|
||||||
|
from novelwriter.common import minmax, simplified
|
||||||
|
from novelwriter.constants import nwItemClass
|
||||||
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DocMerger:
|
class DocMerger:
|
||||||
|
"""Document tool for merging a set of documents into a single new
|
||||||
|
document. The parameters are defined by the user using the
|
||||||
|
GuiDocMerge dialog.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, theProject):
|
def __init__(self, theProject):
|
||||||
|
|
||||||
@@ -122,6 +137,10 @@ class DocMerger:
|
|||||||
|
|
||||||
|
|
||||||
class DocSplitter:
|
class DocSplitter:
|
||||||
|
"""Document tool for splitting a document into a set of new
|
||||||
|
documents. The parameters are defined by the user using the
|
||||||
|
GuiDocSplit dialog.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, theProject, sHandle):
|
def __init__(self, theProject, sHandle):
|
||||||
|
|
||||||
@@ -242,3 +261,197 @@ class DocSplitter:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# END Class DocSplitter
|
# END Class DocSplitter
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectBuilder:
|
||||||
|
"""A class to build a new project from a set of user-defined
|
||||||
|
parameter provided by the New Projecty Wizard.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, mainGui):
|
||||||
|
|
||||||
|
self.mainGui = mainGui
|
||||||
|
self.mainConf = novelwriter.CONFIG
|
||||||
|
|
||||||
|
self.tr = partial(QCoreApplication.translate, "NWProject")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
##
|
||||||
|
# Methods
|
||||||
|
##
|
||||||
|
|
||||||
|
def buildProject(self, data):
|
||||||
|
"""Build a project from a data dictionary of specifications
|
||||||
|
provided by the wizard.
|
||||||
|
"""
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
logger.error("Invalid call to newProject function")
|
||||||
|
return False
|
||||||
|
|
||||||
|
popMinimal = data.get("popMinimal", True)
|
||||||
|
popCustom = data.get("popCustom", False)
|
||||||
|
popSample = data.get("popSample", False)
|
||||||
|
|
||||||
|
# Check if we're extracting the sample project. This is handled
|
||||||
|
# differently as it isn't actually a new project, so we forward
|
||||||
|
# this to another function and return here.
|
||||||
|
if popSample:
|
||||||
|
return self._extractSampleProject(data)
|
||||||
|
|
||||||
|
projPath = data.get("projPath", None)
|
||||||
|
if projPath is None:
|
||||||
|
logger.error("No project path set for the new project")
|
||||||
|
return False
|
||||||
|
|
||||||
|
project = NWProject(self.mainGui)
|
||||||
|
if not project.setProjectPath(projPath, newProject=True):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not project.storage.openProjectInPlace(projPath):
|
||||||
|
return False
|
||||||
|
|
||||||
|
lblNewProject = self.tr("New Project")
|
||||||
|
lblNewChapter = self.tr("New Chapter")
|
||||||
|
lblNewScene = self.tr("New Scene")
|
||||||
|
lblTitlePage = self.tr("Title Page")
|
||||||
|
lblByAuthors = self.tr("By")
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
projName = data.get("projName", lblNewProject)
|
||||||
|
projTitle = data.get("projTitle", lblNewProject)
|
||||||
|
projAuthors = data.get("projAuthors", "")
|
||||||
|
|
||||||
|
project.data.setName(projName)
|
||||||
|
project.data.setTitle(projTitle)
|
||||||
|
project.data.setAuthors(projAuthors)
|
||||||
|
project.setDefaultStatusImport()
|
||||||
|
project._projOpened = int(time())
|
||||||
|
|
||||||
|
# Add Root Folders
|
||||||
|
hNovelRoot = project.newRoot(nwItemClass.NOVEL)
|
||||||
|
hTitlePage = project.newFile(lblTitlePage, hNovelRoot)
|
||||||
|
novelTitle = project.data.title if project.data.title else project.data.name
|
||||||
|
|
||||||
|
titlePage = f"#! {novelTitle}\n\n"
|
||||||
|
if project.data.authors:
|
||||||
|
titlePage += f">> {lblByAuthors} {project.getFormattedAuthors()} <<\n\n"
|
||||||
|
|
||||||
|
aDoc = NWDoc(project, hTitlePage)
|
||||||
|
aDoc.writeDocument(titlePage)
|
||||||
|
|
||||||
|
if popMinimal:
|
||||||
|
# Creating a minimal project with a few root folders and a
|
||||||
|
# single chapter with a single scene.
|
||||||
|
hChapter = project.newFile(lblNewChapter, hNovelRoot)
|
||||||
|
aDoc = NWDoc(project, hChapter)
|
||||||
|
aDoc.writeDocument(f"## {lblNewChapter}\n\n")
|
||||||
|
|
||||||
|
hScene = project.newFile(lblNewScene, hChapter)
|
||||||
|
aDoc = NWDoc(project, hScene)
|
||||||
|
aDoc.writeDocument(f"### {lblNewScene}\n\n")
|
||||||
|
|
||||||
|
project.newRoot(nwItemClass.PLOT)
|
||||||
|
project.newRoot(nwItemClass.CHARACTER)
|
||||||
|
project.newRoot(nwItemClass.WORLD)
|
||||||
|
project.newRoot(nwItemClass.ARCHIVE)
|
||||||
|
|
||||||
|
project.saveProject()
|
||||||
|
project.closeProject()
|
||||||
|
|
||||||
|
elif popCustom:
|
||||||
|
# Create a project structure based on selected root folders
|
||||||
|
# and a number of chapters and scenes selected in the
|
||||||
|
# wizard's custom page.
|
||||||
|
|
||||||
|
# Create chapters and scenes
|
||||||
|
numChapters = data.get("numChapters", 0)
|
||||||
|
numScenes = data.get("numScenes", 0)
|
||||||
|
|
||||||
|
chSynop = self.tr("Summary of the chapter.")
|
||||||
|
scSynop = self.tr("Summary of the scene.")
|
||||||
|
|
||||||
|
# Create chapters
|
||||||
|
if numChapters > 0:
|
||||||
|
for ch in range(numChapters):
|
||||||
|
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
||||||
|
cHandle = project.newFile(chTitle, hNovelRoot)
|
||||||
|
aDoc = NWDoc(project, cHandle)
|
||||||
|
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
||||||
|
|
||||||
|
# Create chapter scenes
|
||||||
|
if numScenes > 0:
|
||||||
|
for sc in range(numScenes):
|
||||||
|
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
||||||
|
sHandle = project.newFile(scTitle, cHandle)
|
||||||
|
aDoc = NWDoc(project, sHandle)
|
||||||
|
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||||
|
|
||||||
|
# Create scenes (no chapters)
|
||||||
|
elif numScenes > 0:
|
||||||
|
for sc in range(numScenes):
|
||||||
|
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
||||||
|
sHandle = project.newFile(scTitle, hNovelRoot)
|
||||||
|
aDoc = NWDoc(project, sHandle)
|
||||||
|
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
||||||
|
|
||||||
|
# Create notes folders
|
||||||
|
noteTitles = {
|
||||||
|
nwItemClass.PLOT: self.tr("Main Plot"),
|
||||||
|
nwItemClass.CHARACTER: self.tr("Protagonist"),
|
||||||
|
nwItemClass.WORLD: self.tr("Main Location"),
|
||||||
|
}
|
||||||
|
|
||||||
|
addNotes = data.get("addNotes", False)
|
||||||
|
for newRoot in data.get("addRoots", []):
|
||||||
|
if newRoot in nwItemClass:
|
||||||
|
rHandle = project.newRoot(newRoot)
|
||||||
|
if addNotes:
|
||||||
|
aHandle = project.newFile(noteTitles[newRoot], rHandle)
|
||||||
|
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
||||||
|
aDoc = NWDoc(project, aHandle)
|
||||||
|
aDoc.writeDocument(f"# {noteTitles[newRoot]}\n\n@tag: {ntTag}\n\n")
|
||||||
|
|
||||||
|
# Also add the archive and trash folders
|
||||||
|
project.newRoot(nwItemClass.ARCHIVE)
|
||||||
|
project.trashFolder()
|
||||||
|
|
||||||
|
project.saveProject()
|
||||||
|
project.closeProject()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
##
|
||||||
|
# Internal Functions
|
||||||
|
##
|
||||||
|
|
||||||
|
def _extractSampleProject(self, data):
|
||||||
|
"""Make a copy of the sample project by extracting the
|
||||||
|
sample.zip file to the new path.
|
||||||
|
"""
|
||||||
|
projPath = data.get("projPath", None)
|
||||||
|
if projPath is None:
|
||||||
|
logger.error("No project path set for the example project")
|
||||||
|
return False
|
||||||
|
|
||||||
|
pkgSample = os.path.join(self.mainConf.assetPath, "sample.zip")
|
||||||
|
if os.path.isfile(pkgSample):
|
||||||
|
try:
|
||||||
|
shutil.unpack_archive(pkgSample, projPath)
|
||||||
|
except Exception as exc:
|
||||||
|
self.mainGui.makeAlert(self.tr(
|
||||||
|
"Failed to create a new example project."
|
||||||
|
), nwAlert.ERROR, exception=exc)
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.mainGui.makeAlert(self.tr(
|
||||||
|
"Failed to create a new example project. "
|
||||||
|
"Could not find the necessary files. "
|
||||||
|
"They seem to be missing from this installation."
|
||||||
|
), nwAlert.ERROR)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
# END Class ProjectBuilder
|
||||||
|
|||||||
+13
-206
@@ -264,150 +264,6 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def newProject(self, projData):
|
|
||||||
"""Create a new project by populating the project tree with a
|
|
||||||
few starter items.
|
|
||||||
"""
|
|
||||||
if not isinstance(projData, dict):
|
|
||||||
logger.error("Invalid call to newProject function")
|
|
||||||
return False
|
|
||||||
|
|
||||||
popMinimal = projData.get("popMinimal", True)
|
|
||||||
popCustom = projData.get("popCustom", False)
|
|
||||||
popSample = projData.get("popSample", False)
|
|
||||||
|
|
||||||
# Check if we're extracting the sample project. This is handled
|
|
||||||
# differently as it isn't actually a new project, so we forward
|
|
||||||
# this to another function and return here.
|
|
||||||
if popSample:
|
|
||||||
return self.extractSampleProject(projData)
|
|
||||||
|
|
||||||
# Project Settings
|
|
||||||
projPath = projData.get("projPath", None)
|
|
||||||
projName = projData.get("projName", self.tr("New Project"))
|
|
||||||
projTitle = projData.get("projTitle", "")
|
|
||||||
projAuthors = projData.get("projAuthors", "")
|
|
||||||
|
|
||||||
if projPath is None:
|
|
||||||
logger.error("No project path set for the new project")
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.clearProject()
|
|
||||||
|
|
||||||
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("Draft"), (200, 150, 0))
|
|
||||||
self._data.itemStatus.write(None, self.tr("Finished"), (50, 200, 0))
|
|
||||||
|
|
||||||
self._data.itemImport.write(None, self.tr("New"), (100, 100, 100))
|
|
||||||
self._data.itemImport.write(None, self.tr("Minor"), (200, 50, 0))
|
|
||||||
self._data.itemImport.write(None, self.tr("Major"), (200, 150, 0))
|
|
||||||
self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0))
|
|
||||||
|
|
||||||
if not self.setProjectPath(projPath, newProject=True):
|
|
||||||
return False
|
|
||||||
|
|
||||||
self._storage.openProjectInPlace(self.projPath)
|
|
||||||
|
|
||||||
self._data.setName(projName)
|
|
||||||
self._data.setTitle(projTitle)
|
|
||||||
self._data.setAuthors(projAuthors)
|
|
||||||
|
|
||||||
hNovelRoot = self.newRoot(nwItemClass.NOVEL)
|
|
||||||
hTitlePage = self.newFile(self.tr("Title Page"), hNovelRoot)
|
|
||||||
|
|
||||||
titlePage = "#! %s\n\n" % (
|
|
||||||
self._data.title if self._data.title else self._data.name
|
|
||||||
)
|
|
||||||
if self._data.authors:
|
|
||||||
titlePage = "%s>> %s %s <<\n" % (
|
|
||||||
titlePage, self.tr("By"), self.getFormattedAuthors()
|
|
||||||
)
|
|
||||||
|
|
||||||
aDoc = NWDoc(self, hTitlePage)
|
|
||||||
aDoc.writeDocument(titlePage)
|
|
||||||
|
|
||||||
if popMinimal:
|
|
||||||
# Creating a minimal project with a few root folders and a
|
|
||||||
# single chapter with a single scene.
|
|
||||||
hChapter = self.newFile(self.tr("New Chapter"), hNovelRoot)
|
|
||||||
aDoc = NWDoc(self, hChapter)
|
|
||||||
aDoc.writeDocument("## %s\n\n" % self.tr("New Chapter"))
|
|
||||||
|
|
||||||
hScene = self.newFile(self.tr("New Scene"), hChapter)
|
|
||||||
aDoc = NWDoc(self, hScene)
|
|
||||||
aDoc.writeDocument("### %s\n\n" % self.tr("New Scene"))
|
|
||||||
|
|
||||||
self.newRoot(nwItemClass.PLOT)
|
|
||||||
self.newRoot(nwItemClass.CHARACTER)
|
|
||||||
self.newRoot(nwItemClass.WORLD)
|
|
||||||
self.newRoot(nwItemClass.ARCHIVE)
|
|
||||||
|
|
||||||
elif popCustom:
|
|
||||||
# Create a project structure based on selected root folders
|
|
||||||
# and a number of chapters and scenes selected in the
|
|
||||||
# wizard's custom page.
|
|
||||||
|
|
||||||
# Create chapters and scenes
|
|
||||||
numChapters = projData.get("numChapters", 0)
|
|
||||||
numScenes = projData.get("numScenes", 0)
|
|
||||||
|
|
||||||
chSynop = self.tr("Summary of the chapter.")
|
|
||||||
scSynop = self.tr("Summary of the scene.")
|
|
||||||
|
|
||||||
# Create chapters
|
|
||||||
if numChapters > 0:
|
|
||||||
for ch in range(numChapters):
|
|
||||||
chTitle = self.tr("Chapter {0}").format(f"{ch+1:d}")
|
|
||||||
cHandle = self.newFile(chTitle, hNovelRoot)
|
|
||||||
aDoc = NWDoc(self, cHandle)
|
|
||||||
aDoc.writeDocument(f"## {chTitle}\n\n% Synopsis: {chSynop}\n\n")
|
|
||||||
|
|
||||||
# Create chapter scenes
|
|
||||||
if numScenes > 0:
|
|
||||||
for sc in range(numScenes):
|
|
||||||
scTitle = self.tr("Scene {0}").format(f"{ch+1:d}.{sc+1:d}")
|
|
||||||
sHandle = self.newFile(scTitle, cHandle)
|
|
||||||
aDoc = NWDoc(self, sHandle)
|
|
||||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
|
||||||
|
|
||||||
# Create scenes (no chapters)
|
|
||||||
elif numScenes > 0:
|
|
||||||
for sc in range(numScenes):
|
|
||||||
scTitle = self.tr("Scene {0}").format(f"{sc+1:d}")
|
|
||||||
sHandle = self.newFile(scTitle, hNovelRoot)
|
|
||||||
aDoc = NWDoc(self, sHandle)
|
|
||||||
aDoc.writeDocument(f"### {scTitle}\n\n% Synopsis: {scSynop}\n\n")
|
|
||||||
|
|
||||||
# Create notes folders
|
|
||||||
noteTitles = {
|
|
||||||
nwItemClass.PLOT: self.tr("Main Plot"),
|
|
||||||
nwItemClass.CHARACTER: self.tr("Protagonist"),
|
|
||||||
nwItemClass.WORLD: self.tr("Main Location"),
|
|
||||||
}
|
|
||||||
|
|
||||||
addNotes = projData.get("addNotes", False)
|
|
||||||
for newRoot in projData.get("addRoots", []):
|
|
||||||
if newRoot in nwItemClass:
|
|
||||||
rHandle = self.newRoot(newRoot)
|
|
||||||
if addNotes:
|
|
||||||
aHandle = self.newFile(noteTitles[newRoot], rHandle)
|
|
||||||
ntTag = simplified(noteTitles[newRoot]).replace(" ", "")
|
|
||||||
aDoc = NWDoc(self, aHandle)
|
|
||||||
aDoc.writeDocument(f"# {noteTitles[newRoot]}\n\n@tag: {ntTag}\n\n")
|
|
||||||
|
|
||||||
# Also add the archive and trash folders
|
|
||||||
self.newRoot(nwItemClass.ARCHIVE)
|
|
||||||
self.trashFolder()
|
|
||||||
|
|
||||||
# Finalise
|
|
||||||
if popCustom or popMinimal:
|
|
||||||
self._projOpened = time()
|
|
||||||
self.setProjectChanged(True)
|
|
||||||
self.saveProject(autoSave=True)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def openProject(self, fileName, overrideLock=False):
|
def openProject(self, fileName, overrideLock=False):
|
||||||
"""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,
|
||||||
@@ -680,6 +536,19 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def setDefaultStatusImport(self):
|
||||||
|
"""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("Note"), (200, 50, 0))
|
||||||
|
self._data.itemStatus.write(None, self.tr("Draft"), (200, 150, 0))
|
||||||
|
self._data.itemStatus.write(None, self.tr("Finished"), (50, 200, 0))
|
||||||
|
self._data.itemImport.write(None, self.tr("New"), (100, 100, 100))
|
||||||
|
self._data.itemImport.write(None, self.tr("Minor"), (200, 50, 0))
|
||||||
|
self._data.itemImport.write(None, self.tr("Major"), (200, 150, 0))
|
||||||
|
self._data.itemImport.write(None, self.tr("Main"), (50, 200, 0))
|
||||||
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
# Zip/Unzip Project
|
# Zip/Unzip Project
|
||||||
##
|
##
|
||||||
@@ -753,68 +622,6 @@ class NWProject(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def extractSampleProject(self, projData):
|
|
||||||
"""Make a copy of the sample project.
|
|
||||||
First, look for the sample.zip file in the assets folder and
|
|
||||||
unpack it. If it doesn't exist, try to copy the content of the
|
|
||||||
sample folder to the new project path. If neither exits, error.
|
|
||||||
"""
|
|
||||||
projPath = projData.get("projPath", None)
|
|
||||||
if projPath is None:
|
|
||||||
logger.error("No project path set for the example project")
|
|
||||||
return False
|
|
||||||
|
|
||||||
srcSample = os.path.abspath(os.path.join(self.mainConf.appRoot, "sample"))
|
|
||||||
pkgSample = os.path.join(self.mainConf.assetPath, "sample.zip")
|
|
||||||
|
|
||||||
isSuccess = False
|
|
||||||
if os.path.isfile(pkgSample):
|
|
||||||
|
|
||||||
self.setProjectPath(projPath, newProject=True)
|
|
||||||
try:
|
|
||||||
shutil.unpack_archive(pkgSample, projPath)
|
|
||||||
isSuccess = True
|
|
||||||
except Exception as exc:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Failed to create a new example project."
|
|
||||||
), nwAlert.ERROR, exception=exc)
|
|
||||||
|
|
||||||
elif os.path.isdir(srcSample):
|
|
||||||
|
|
||||||
self.setProjectPath(projPath, newProject=True)
|
|
||||||
try:
|
|
||||||
srcProj = os.path.join(srcSample, nwFiles.PROJ_FILE)
|
|
||||||
dstProj = os.path.join(projPath, nwFiles.PROJ_FILE)
|
|
||||||
shutil.copyfile(srcProj, dstProj)
|
|
||||||
|
|
||||||
srcContent = os.path.join(srcSample, "content")
|
|
||||||
dstContent = os.path.join(projPath, "content")
|
|
||||||
for srcFile in os.listdir(srcContent):
|
|
||||||
srcDoc = os.path.join(srcContent, srcFile)
|
|
||||||
dstDoc = os.path.join(dstContent, srcFile)
|
|
||||||
shutil.copyfile(srcDoc, dstDoc)
|
|
||||||
|
|
||||||
isSuccess = True
|
|
||||||
|
|
||||||
except Exception as exc:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Failed to create a new example project."
|
|
||||||
), nwAlert.ERROR, exception=exc)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.mainGui.makeAlert(self.tr(
|
|
||||||
"Failed to create a new example project. "
|
|
||||||
"Could not find the necessary files. "
|
|
||||||
"They seem to be missing from this installation."
|
|
||||||
), nwAlert.ERROR)
|
|
||||||
|
|
||||||
if isSuccess:
|
|
||||||
self.clearProject()
|
|
||||||
self.mainGui.openProject(projPath)
|
|
||||||
self.mainGui.rebuildIndex()
|
|
||||||
|
|
||||||
return isSuccess
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Setters
|
# Setters
|
||||||
##
|
##
|
||||||
|
|||||||
+5
-25
@@ -50,9 +50,9 @@ from novelwriter.dialogs import (
|
|||||||
from novelwriter.tools import (
|
from novelwriter.tools import (
|
||||||
GuiBuildNovel, GuiLipsum, GuiProjectWizard, GuiWritingStats
|
GuiBuildNovel, GuiLipsum, GuiProjectWizard, GuiWritingStats
|
||||||
)
|
)
|
||||||
from novelwriter.core import NWProject
|
from novelwriter.core import NWProject, ProjectBuilder
|
||||||
from novelwriter.enum import (
|
from novelwriter.enum import (
|
||||||
nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwState, nwView
|
nwDocMode, nwItemType, nwItemClass, nwAlert, nwWidget, nwView
|
||||||
)
|
)
|
||||||
from novelwriter.common import getGuiItem, hexToInt
|
from novelwriter.common import getGuiItem, hexToInt
|
||||||
from novelwriter.constants import nwFiles
|
from novelwriter.constants import nwFiles
|
||||||
@@ -365,30 +365,10 @@ class GuiMain(QMainWindow):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
logger.info("Creating new project")
|
logger.info("Creating new project")
|
||||||
if self.theProject.newProject(projData):
|
nwProject = ProjectBuilder(self)
|
||||||
|
if nwProject.buildProject(projData):
|
||||||
self.hasProject = True
|
self.openProject(projPath)
|
||||||
self.idleRefTime = time()
|
|
||||||
self.idleTime = 0.0
|
|
||||||
|
|
||||||
self.rebuildTrees()
|
|
||||||
self.saveProject()
|
|
||||||
|
|
||||||
self.docEditor.setDictionaries()
|
|
||||||
self.projView.openProjectTasks()
|
|
||||||
self.novelView.openProjectTasks()
|
|
||||||
self.outlineView.openProjectTasks()
|
|
||||||
self.rebuildIndex(beQuiet=True)
|
|
||||||
|
|
||||||
self.mainStatus.setRefTime(self.theProject.projOpened)
|
|
||||||
self.mainStatus.setProjectStatus(nwState.GOOD)
|
|
||||||
self.mainStatus.setDocumentStatus(nwState.NONE)
|
|
||||||
self.mainStatus.setStatus(self.tr("New project created ..."))
|
|
||||||
|
|
||||||
self._updateWindowTitle(self.theProject.data.name)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.theProject.clearProject()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-03 23:38:30">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
<author>John Doh</author>
|
<author>John Doh</author>
|
||||||
<saveCount>1</saveCount>
|
<saveCount>1</saveCount>
|
||||||
<autoCount>1</autoCount>
|
<autoCount>0</autoCount>
|
||||||
<editTime>0</editTime>
|
<editTime>0</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-03 23:37:43">
|
||||||
<project>
|
<project>
|
||||||
<name>Test Custom</name>
|
<name>Test Custom</name>
|
||||||
<title>Test Novel</title>
|
<title>Test Novel</title>
|
||||||
<author>Jane Doe</author>
|
<author>Jane Doe</author>
|
||||||
<author>John Doh</author>
|
<author>John Doh</author>
|
||||||
<saveCount>1</saveCount>
|
<saveCount>1</saveCount>
|
||||||
<autoCount>1</autoCount>
|
<autoCount>0</autoCount>
|
||||||
<editTime>0</editTime>
|
<editTime>0</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
+4
-4
@@ -1,10 +1,10 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
|
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-03 23:33:20">
|
||||||
<project>
|
<project>
|
||||||
<name>New Project</name>
|
<name>New Project</name>
|
||||||
<title>None</title>
|
<title>New Project</title>
|
||||||
<saveCount>2</saveCount>
|
<saveCount>1</saveCount>
|
||||||
<autoCount>1</autoCount>
|
<autoCount>0</autoCount>
|
||||||
<editTime>0</editTime>
|
<editTime>0</editTime>
|
||||||
</project>
|
</project>
|
||||||
<settings>
|
<settings>
|
||||||
@@ -23,13 +23,15 @@ import os
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
from tools import C, buildTestProject, cmpFiles
|
from tools import C, buildTestProject, cmpFiles, XML_IGNORE
|
||||||
|
|
||||||
|
from novelwriter.constants import nwItemClass
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.document import NWDoc
|
from novelwriter.core.document import NWDoc
|
||||||
from novelwriter.core.coretools import DocMerger, DocSplitter
|
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
@@ -259,3 +261,153 @@ def testCoreTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, mock
|
|||||||
theProject.saveProject()
|
theProject.saveProject()
|
||||||
|
|
||||||
# END Test testCoreTools_DocSplitter
|
# END Test testCoreTools_DocSplitter
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreTools_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||||
|
"""Create a new project from a project wizard dictionary. With
|
||||||
|
default setting, creating a Minimal project.
|
||||||
|
"""
|
||||||
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
testFile = os.path.join(outDir, "coreTools_NewMinimal_nwProject.nwx")
|
||||||
|
compFile = os.path.join(refDir, "coreTools_NewMinimal_nwProject.nwx")
|
||||||
|
|
||||||
|
projBuild = ProjectBuilder(mockGUI)
|
||||||
|
|
||||||
|
# Setting no data should fail
|
||||||
|
assert projBuild.buildProject({}) is False
|
||||||
|
|
||||||
|
# Wrong type should also fail
|
||||||
|
assert projBuild.buildProject("stuff") is False
|
||||||
|
|
||||||
|
# Try again with a proper path
|
||||||
|
assert projBuild.buildProject({"projPath": fncDir}) is True
|
||||||
|
|
||||||
|
# Creating the project once more should fail
|
||||||
|
assert projBuild.buildProject({"projPath": fncDir}) is False
|
||||||
|
|
||||||
|
# Save and close
|
||||||
|
copyfile(projFile, testFile)
|
||||||
|
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||||
|
|
||||||
|
# END Test testCoreTools_NewMinimal
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreTools_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||||
|
"""Create a new project from a project wizard dictionary.
|
||||||
|
Custom type with chapters and scenes.
|
||||||
|
"""
|
||||||
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
testFile = os.path.join(outDir, "coreTools_NewCustomA_nwProject.nwx")
|
||||||
|
compFile = os.path.join(refDir, "coreTools_NewCustomA_nwProject.nwx")
|
||||||
|
|
||||||
|
projData = {
|
||||||
|
"projName": "Test Custom",
|
||||||
|
"projTitle": "Test Novel",
|
||||||
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
||||||
|
"projPath": fncDir,
|
||||||
|
"popSample": False,
|
||||||
|
"popMinimal": False,
|
||||||
|
"popCustom": True,
|
||||||
|
"addRoots": [
|
||||||
|
nwItemClass.PLOT,
|
||||||
|
nwItemClass.CHARACTER,
|
||||||
|
nwItemClass.WORLD,
|
||||||
|
],
|
||||||
|
"addNotes": True,
|
||||||
|
"numChapters": 3,
|
||||||
|
"numScenes": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
projBuild = ProjectBuilder(mockGUI)
|
||||||
|
assert projBuild.buildProject(projData) is True
|
||||||
|
|
||||||
|
copyfile(projFile, testFile)
|
||||||
|
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||||
|
|
||||||
|
# END Test testCoreTools_NewCustomA
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreTools_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||||
|
"""Create a new project from a project wizard dictionary.
|
||||||
|
Custom type without chapters, but with scenes.
|
||||||
|
"""
|
||||||
|
projFile = os.path.join(fncDir, "nwProject.nwx")
|
||||||
|
testFile = os.path.join(outDir, "coreTools_NewCustomB_nwProject.nwx")
|
||||||
|
compFile = os.path.join(refDir, "coreTools_NewCustomB_nwProject.nwx")
|
||||||
|
|
||||||
|
projData = {
|
||||||
|
"projName": "Test Custom",
|
||||||
|
"projTitle": "Test Novel",
|
||||||
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
||||||
|
"projPath": fncDir,
|
||||||
|
"popSample": False,
|
||||||
|
"popMinimal": False,
|
||||||
|
"popCustom": True,
|
||||||
|
"addRoots": [
|
||||||
|
nwItemClass.PLOT,
|
||||||
|
nwItemClass.CHARACTER,
|
||||||
|
nwItemClass.WORLD,
|
||||||
|
],
|
||||||
|
"addNotes": True,
|
||||||
|
"numChapters": 0,
|
||||||
|
"numScenes": 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
projBuild = ProjectBuilder(mockGUI)
|
||||||
|
assert projBuild.buildProject(projData) is True
|
||||||
|
|
||||||
|
copyfile(projFile, testFile)
|
||||||
|
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
||||||
|
|
||||||
|
# END Test testCoreTools_NewCustomB
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testCoreTools_NewSample(fncDir, tmpConf, mockGUI, tmpDir):
|
||||||
|
"""Check that we can create a new project can be created from the
|
||||||
|
provided sample project via a zip file.
|
||||||
|
"""
|
||||||
|
projData = {
|
||||||
|
"projName": "Test Sample",
|
||||||
|
"projTitle": "Test Novel",
|
||||||
|
"projAuthors": "Jane Doe\nJohn Doh\n",
|
||||||
|
"projPath": fncDir,
|
||||||
|
"popSample": True,
|
||||||
|
"popMinimal": False,
|
||||||
|
"popCustom": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
projBuild = ProjectBuilder(mockGUI)
|
||||||
|
|
||||||
|
# No path set
|
||||||
|
assert projBuild.buildProject({"popSample": True}) is False
|
||||||
|
|
||||||
|
# Force the lookup path for assets to our temp folder
|
||||||
|
srcSample = os.path.abspath(os.path.join(tmpConf.appRoot, "sample"))
|
||||||
|
dstSample = os.path.join(tmpDir, "sample.zip")
|
||||||
|
tmpConf.assetPath = tmpDir
|
||||||
|
|
||||||
|
# Cannot extract when the zip does not exist
|
||||||
|
assert projBuild.buildProject(projData) is False
|
||||||
|
|
||||||
|
# Create and open a defective zip file
|
||||||
|
with open(dstSample, mode="w+") as outFile:
|
||||||
|
outFile.write("foo")
|
||||||
|
|
||||||
|
assert projBuild.buildProject(projData) is False
|
||||||
|
os.unlink(dstSample)
|
||||||
|
|
||||||
|
# Create a real zip file, and unpack it
|
||||||
|
with ZipFile(dstSample, "w") as zipObj:
|
||||||
|
zipObj.write(os.path.join(srcSample, "nwProject.nwx"), "nwProject.nwx")
|
||||||
|
for docFile in os.listdir(os.path.join(srcSample, "content")):
|
||||||
|
srcDoc = os.path.join(srcSample, "content", docFile)
|
||||||
|
zipObj.write(srcDoc, "content/"+docFile)
|
||||||
|
|
||||||
|
assert projBuild.buildProject(projData) is True
|
||||||
|
os.unlink(dstSample)
|
||||||
|
|
||||||
|
# END Test testCoreTools_NewSample
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from shutil import copyfile
|
|||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from mock import causeOSError
|
from mock import causeOSError
|
||||||
from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE, C
|
from tools import C, cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE
|
||||||
|
|
||||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||||
from novelwriter.common import formatTimeStamp
|
from novelwriter.common import formatTimeStamp
|
||||||
@@ -40,214 +40,6 @@ from novelwriter.core.document import NWDoc
|
|||||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter, XMLReadState
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|
||||||
"""Create a new project from a project wizard dictionary. With
|
|
||||||
default setting, creating a Minimal project.
|
|
||||||
"""
|
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
|
||||||
testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
|
|
||||||
compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")
|
|
||||||
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
|
|
||||||
# Setting no data should fail
|
|
||||||
assert theProject.newProject({}) is False
|
|
||||||
|
|
||||||
# Wrong type should also fail
|
|
||||||
assert theProject.newProject("stuff") is False
|
|
||||||
|
|
||||||
# Try again with a proper path
|
|
||||||
assert theProject.newProject({"projPath": fncDir}) is True
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
|
|
||||||
# Creating the project once more should fail
|
|
||||||
assert theProject.newProject({"projPath": fncDir}) is False
|
|
||||||
|
|
||||||
# Open again
|
|
||||||
assert theProject.openProject(projFile) is True
|
|
||||||
|
|
||||||
# Save and close
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
copyfile(projFile, testFile)
|
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
|
||||||
assert theProject.projChanged is False
|
|
||||||
|
|
||||||
# Open a second time
|
|
||||||
assert theProject.openProject(projFile) is True
|
|
||||||
assert theProject.openProject(projFile) is False
|
|
||||||
assert theProject.openProject(projFile, overrideLock=True) is True
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
copyfile(projFile, testFile)
|
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
|
||||||
|
|
||||||
# END Test testCoreProject_NewMinimal
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|
||||||
"""Create a new project from a project wizard dictionary.
|
|
||||||
Custom type with chapters and scenes.
|
|
||||||
"""
|
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
|
||||||
testFile = os.path.join(outDir, "coreProject_NewCustomA_nwProject.nwx")
|
|
||||||
compFile = os.path.join(refDir, "coreProject_NewCustomA_nwProject.nwx")
|
|
||||||
|
|
||||||
projData = {
|
|
||||||
"projName": "Test Custom",
|
|
||||||
"projTitle": "Test Novel",
|
|
||||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
||||||
"projPath": fncDir,
|
|
||||||
"popSample": False,
|
|
||||||
"popMinimal": False,
|
|
||||||
"popCustom": True,
|
|
||||||
"addRoots": [
|
|
||||||
nwItemClass.PLOT,
|
|
||||||
nwItemClass.CHARACTER,
|
|
||||||
nwItemClass.WORLD,
|
|
||||||
],
|
|
||||||
"addNotes": True,
|
|
||||||
"numChapters": 3,
|
|
||||||
"numScenes": 3,
|
|
||||||
}
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
|
|
||||||
assert theProject.newProject(projData) is True
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
|
|
||||||
copyfile(projFile, testFile)
|
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
|
||||||
|
|
||||||
# END Test testCoreProject_NewCustomA
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
|
|
||||||
"""Create a new project from a project wizard dictionary.
|
|
||||||
Custom type without chapters, but with scenes.
|
|
||||||
"""
|
|
||||||
projFile = os.path.join(fncDir, "nwProject.nwx")
|
|
||||||
testFile = os.path.join(outDir, "coreProject_NewCustomB_nwProject.nwx")
|
|
||||||
compFile = os.path.join(refDir, "coreProject_NewCustomB_nwProject.nwx")
|
|
||||||
|
|
||||||
projData = {
|
|
||||||
"projName": "Test Custom",
|
|
||||||
"projTitle": "Test Novel",
|
|
||||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
||||||
"projPath": fncDir,
|
|
||||||
"popSample": False,
|
|
||||||
"popMinimal": False,
|
|
||||||
"popCustom": True,
|
|
||||||
"addRoots": [
|
|
||||||
nwItemClass.PLOT,
|
|
||||||
nwItemClass.CHARACTER,
|
|
||||||
nwItemClass.WORLD,
|
|
||||||
],
|
|
||||||
"addNotes": True,
|
|
||||||
"numChapters": 0,
|
|
||||||
"numScenes": 6,
|
|
||||||
}
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
|
|
||||||
assert theProject.newProject(projData) is True
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
|
|
||||||
copyfile(projFile, testFile)
|
|
||||||
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
|
|
||||||
|
|
||||||
# END Test testCoreProject_NewCustomB
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
|
|
||||||
"""Check that we can create a new project can be created from the
|
|
||||||
provided sample project via a zip file.
|
|
||||||
"""
|
|
||||||
projData = {
|
|
||||||
"projName": "Test Sample",
|
|
||||||
"projTitle": "Test Novel",
|
|
||||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
||||||
"projPath": fncDir,
|
|
||||||
"popSample": True,
|
|
||||||
"popMinimal": False,
|
|
||||||
"popCustom": False,
|
|
||||||
}
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
|
|
||||||
# Sample set, but no path
|
|
||||||
assert not theProject.newProject({"popSample": True})
|
|
||||||
|
|
||||||
# Force the lookup path for assets to our temp folder
|
|
||||||
srcSample = os.path.abspath(os.path.join(tmpConf.appRoot, "sample"))
|
|
||||||
dstSample = os.path.join(tmpDir, "sample.zip")
|
|
||||||
tmpConf.assetPath = tmpDir
|
|
||||||
|
|
||||||
# Create and open a defective zip file
|
|
||||||
with open(dstSample, mode="w+") as outFile:
|
|
||||||
outFile.write("foo")
|
|
||||||
|
|
||||||
assert not theProject.newProject(projData)
|
|
||||||
os.unlink(dstSample)
|
|
||||||
|
|
||||||
# Create a real zip file, and unpack it
|
|
||||||
with ZipFile(dstSample, "w") as zipObj:
|
|
||||||
zipObj.write(os.path.join(srcSample, "nwProject.nwx"), "nwProject.nwx")
|
|
||||||
for docFile in os.listdir(os.path.join(srcSample, "content")):
|
|
||||||
srcDoc = os.path.join(srcSample, "content", docFile)
|
|
||||||
zipObj.write(srcDoc, "content/"+docFile)
|
|
||||||
|
|
||||||
assert theProject.newProject(projData) is True
|
|
||||||
assert theProject.openProject(fncDir) is True
|
|
||||||
assert theProject.data.name == "Sample Project"
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
os.unlink(dstSample)
|
|
||||||
|
|
||||||
# END Test testCoreProject_NewSampleA
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
|
||||||
def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
|
|
||||||
"""Check that we can create a new project can be created from the
|
|
||||||
provided sample project folder.
|
|
||||||
"""
|
|
||||||
projData = {
|
|
||||||
"projName": "Test Sample",
|
|
||||||
"projTitle": "Test Novel",
|
|
||||||
"projAuthors": "Jane Doe\nJohn Doh\n",
|
|
||||||
"projPath": fncDir,
|
|
||||||
"popSample": True,
|
|
||||||
"popMinimal": False,
|
|
||||||
"popCustom": False,
|
|
||||||
}
|
|
||||||
theProject = NWProject(mockGUI)
|
|
||||||
|
|
||||||
# Make sure we do not pick up the novelwriter/assets/sample.zip file
|
|
||||||
tmpConf.assetPath = tmpDir
|
|
||||||
|
|
||||||
# Set a fake project file name
|
|
||||||
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nothing.nwx")
|
|
||||||
assert not theProject.newProject(projData)
|
|
||||||
|
|
||||||
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
|
|
||||||
assert theProject.newProject(projData) is True
|
|
||||||
assert theProject.openProject(fncDir) is True
|
|
||||||
assert theProject.data.name == "Sample Project"
|
|
||||||
assert theProject.saveProject() is True
|
|
||||||
assert theProject.closeProject() is True
|
|
||||||
|
|
||||||
# Misdirect the appRoot path so neither is possible
|
|
||||||
tmpConf.appRoot = tmpDir
|
|
||||||
assert not theProject.newProject(projData)
|
|
||||||
|
|
||||||
# END Test testCoreProject_NewSampleB
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
|
||||||
"""Check that new root folders can be added to the project.
|
"""Check that new root folders can be added to the project.
|
||||||
|
|||||||
+1
-9
@@ -168,15 +168,7 @@ def buildTestProject(theObject, projPath):
|
|||||||
theProject.clearProject()
|
theProject.clearProject()
|
||||||
theProject.setProjectPath(projPath, newProject=True)
|
theProject.setProjectPath(projPath, newProject=True)
|
||||||
theProject.storage.openProjectInPlace(theProject.projPath)
|
theProject.storage.openProjectInPlace(theProject.projPath)
|
||||||
|
theProject.setDefaultStatusImport()
|
||||||
theProject.data.itemStatus.write(None, "New", (100, 100, 100))
|
|
||||||
theProject.data.itemStatus.write(None, "Note", (200, 50, 0))
|
|
||||||
theProject.data.itemStatus.write(None, "Draft", (200, 150, 0))
|
|
||||||
theProject.data.itemStatus.write(None, "Finished", (50, 200, 0))
|
|
||||||
theProject.data.itemImport.write(None, "New", (100, 100, 100))
|
|
||||||
theProject.data.itemImport.write(None, "Minor", (200, 50, 0))
|
|
||||||
theProject.data.itemImport.write(None, "Major", (200, 150, 0))
|
|
||||||
theProject.data.itemImport.write(None, "Main", (50, 200, 0))
|
|
||||||
|
|
||||||
theProject.data.setName("New Project")
|
theProject.data.setName("New Project")
|
||||||
theProject.data.setTitle("New Novel")
|
theProject.data.setTitle("New Novel")
|
||||||
|
|||||||
Reference in New Issue
Block a user