Move new project tool to core tools (resolves #1152)

This commit is contained in:
Veronica Berglyd Olsen
2022-11-03 23:47:51 +01:00
parent bd81ea0880
commit 2e590b742f
10 changed files with 398 additions and 461 deletions
+2 -1
View File
@@ -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/>.
"""
from novelwriter.core.coretools import DocMerger, DocSplitter
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
from novelwriter.core.document import NWDoc
from novelwriter.core.index import countWords
from novelwriter.core.project import NWProject
@@ -31,6 +31,7 @@ from novelwriter.core.tomd import ToMarkdown
__all__ = [
"DocMerger",
"DocSplitter",
"ProjectBuilder",
"countWords",
"NWDoc",
"NWProject",
+214 -1
View File
@@ -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/>.
"""
import os
import shutil
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
logger = logging.getLogger(__name__)
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):
@@ -122,6 +137,10 @@ class DocMerger:
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):
@@ -242,3 +261,197 @@ class DocSplitter:
return
# 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
View File
@@ -264,150 +264,6 @@ class NWProject(QObject):
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):
"""Open the project file provided. If it doesn't exist, assume
it is a folder and look for the file within it. If successful,
@@ -680,6 +536,19 @@ class NWProject(QObject):
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
##
@@ -753,68 +622,6 @@ class NWProject(QObject):
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
##
+5 -25
View File
@@ -50,9 +50,9 @@ from novelwriter.dialogs import (
from novelwriter.tools import (
GuiBuildNovel, GuiLipsum, GuiProjectWizard, GuiWritingStats
)
from novelwriter.core import NWProject
from novelwriter.core import NWProject, ProjectBuilder
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.constants import nwFiles
@@ -365,30 +365,10 @@ class GuiMain(QMainWindow):
return False
logger.info("Creating new project")
if self.theProject.newProject(projData):
self.hasProject = True
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)
nwProject = ProjectBuilder(self)
if nwProject.buildProject(projData):
self.openProject(projPath)
else:
self.theProject.clearProject()
return False
return True
@@ -1,12 +1,12 @@
<?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>
<name>Test Custom</name>
<title>Test Novel</title>
<author>Jane Doe</author>
<author>John Doh</author>
<saveCount>1</saveCount>
<autoCount>1</autoCount>
<autoCount>0</autoCount>
<editTime>0</editTime>
</project>
<settings>
@@ -1,12 +1,12 @@
<?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>
<name>Test Custom</name>
<title>Test Novel</title>
<author>Jane Doe</author>
<author>John Doh</author>
<saveCount>1</saveCount>
<autoCount>1</autoCount>
<autoCount>0</autoCount>
<editTime>0</editTime>
</project>
<settings>
@@ -1,10 +1,10 @@
<?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>
<name>New Project</name>
<title>None</title>
<saveCount>2</saveCount>
<autoCount>1</autoCount>
<title>New Project</title>
<saveCount>1</saveCount>
<autoCount>0</autoCount>
<editTime>0</editTime>
</project>
<settings>
+154 -2
View File
@@ -23,13 +23,15 @@ import os
import pytest
from shutil import copyfile
from zipfile import ZipFile
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.document import NWDoc
from novelwriter.core.coretools import DocMerger, DocSplitter
from novelwriter.core.coretools import DocMerger, DocSplitter, ProjectBuilder
@pytest.mark.core
@@ -259,3 +261,153 @@ def testCoreTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, mock
theProject.saveProject()
# 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
+1 -209
View File
@@ -27,7 +27,7 @@ from shutil import copyfile
from zipfile import ZipFile
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.common import formatTimeStamp
@@ -40,214 +40,6 @@ from novelwriter.core.document import NWDoc
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
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI, mockRnd):
"""Check that new root folders can be added to the project.
+1 -9
View File
@@ -168,15 +168,7 @@ def buildTestProject(theObject, projPath):
theProject.clearProject()
theProject.setProjectPath(projPath, newProject=True)
theProject.storage.openProjectInPlace(theProject.projPath)
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.setDefaultStatusImport()
theProject.data.setName("New Project")
theProject.data.setTitle("New Novel")