Add a UUID to projects

This commit is contained in:
Veronica Berglyd Olsen
2022-11-05 17:03:45 +01:00
parent 8b4dac4e13
commit c6d3f680cc
22 changed files with 91 additions and 40 deletions
+10
View File
@@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import os
import json
import uuid
import hashlib
import logging
@@ -113,6 +114,15 @@ def checkHandle(value, default, allowNone=False):
return default
def checkUuid(value, default):
"""Try to process a value as an uuid, or return a default.
"""
try:
return str(uuid.UUID(value))
except Exception:
return default
# =============================================================================================== #
# Validator Functions
# =============================================================================================== #
+1
View File
@@ -322,6 +322,7 @@ class ProjectBuilder:
projTitle = data.get("projTitle", lblNewProject)
projAuthors = data.get("projAuthors", "")
project.data.setUuid(None)
project.data.setName(projName)
project.data.setTitle(projTitle)
project.data.setAuthors(projAuthors)
+15 -1
View File
@@ -25,9 +25,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from __future__ import annotations
import uuid
import logging
from novelwriter.common import checkBool, checkInt, checkStringNone, isHandle, simplified
from novelwriter.common import (
checkBool, checkInt, checkStringNone, checkUuid, isHandle, simplified
)
from novelwriter.core.status import NWStatus
logger = logging.getLogger(__name__)
@@ -196,6 +199,17 @@ class NWProjectData:
# Setters
##
def setUuid(self, value):
"""Set the project id.
"""
value = checkUuid(value, "")
if not value:
self._uuid = str(uuid.uuid4())
elif value != self._uuid:
self._uuid = value
self.theProject.setProjectChanged(True)
return
def setName(self, value):
"""Set a new project name.
"""
+2 -1
View File
@@ -224,6 +224,7 @@ class ProjectXMLReader:
"""Parse the project section of the XML file.
"""
logger.debug("Parsing <project> section")
projData.setUuid(xSection.attrib.get("id", None))
for xItem in xSection:
if xItem.tag == "name":
projData.setName(xItem.text)
@@ -476,7 +477,7 @@ class ProjectXMLWriter:
})
# Save Project Meta
xProject = etree.SubElement(xRoot, "project")
xProject = etree.SubElement(xRoot, "project", attrib={"id": projData.uuid})
self._packSingleValue(xProject, "name", projData.name)
self._packSingleValue(xProject, "title", projData.title)
self._packListValue(xProject, "author", projData.authors)
+4 -4
View File
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:20:53">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:59:57">
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
<author>Jay Doh</author>
<saveCount>1409</saveCount>
<saveCount>1421</saveCount>
<autoCount>236</autoCount>
<editTime>69427</editTime>
<editTime>69454</editTime>
</project>
<settings>
<doBackup>False</doBackup>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 12:20:53">
<project>
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+4 -5
View File
@@ -1,19 +1,18 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-31 22:17:38">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:38:19">
<project id="5ac0df12-8b8b-476b-9905-21d33685687c">
<name>Lorem Ipsum</name>
<title>Lorem Ipsum</title>
<author>lipsum.com</author>
<saveCount>32</saveCount>
<saveCount>34</saveCount>
<autoCount>24</autoCount>
<editTime>1889</editTime>
<editTime>1893</editTime>
</project>
<settings>
<doBackup>False</doBackup>
<language>en_GB</language>
<spellCheck>False</spellCheck>
<spellLang>None</spellLang>
<totalWordCount>3847</totalWordCount>
<novelWordCount>3109</novelWordCount>
<notesWordCount>738</notesWordCount>
<lastHandle>
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:43:09">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>New Project</name>
<title>New Novel</title>
<author>Jane Doe</author>
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:22">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:43:09">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>New Project</name>
<title>New Novel</title>
<author>Jane Doe</author>
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-03 23:38:30">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:57:31">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Test Custom</name>
<title>Test Novel</title>
<author>Jane Doe</author>
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-03 23:37:43">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:58:05">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Test Custom</name>
<title>Test Novel</title>
<author>Jane Doe</author>
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-03 23:33:20">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:58:05">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>New Project</name>
<title>New Project</title>
<saveCount>1</saveCount>
@@ -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 12:01:37">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:40:49">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>New Project</name>
<title>New Novel</title>
<author>Jane Doe</author>
<saveCount>4</saveCount>
<autoCount>2</autoCount>
<editTime>3</editTime>
<editTime>4</editTime>
</project>
<settings>
<doBackup>True</doBackup>
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-01 11:53:30">
<project>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-11-05 16:37:48">
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>New Project</name>
<title>New Novel</title>
<author>Jane Doe</author>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2020-05-28 09:59:15">
<project>
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2020-06-26 21:20:24">
<project>
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2021-08-30 23:33:44">
<project>
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="2.0-rc1" hexVersion="0x020000c1" fileVersion="1.4" timeStamp="2022-10-25 18:26:15">
<project>
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed">
<name>Sample Project</name>
<title>Sample Project</title>
<author>Jane Smith</author>
+19 -5
View File
@@ -30,11 +30,11 @@ from tools import writeFile
from novelwriter.guimain import GuiMain
from novelwriter.common import (
checkStringNone, checkString, checkInt, checkFloat, checkBool, checkHandle,
isHandle, isTitleTag, isItemClass, isItemType, isItemLayout, hexToInt,
minmax, checkIntTuple, formatInt, formatTimeStamp, formatTime, simplified,
splitVersionNumber, transferCase, fuzzyTime, numberToRoman, jsonEncode,
readTextFile, makeFileNameSafe, ensureFolder, sha256sum, getGuiItem,
NWConfigParser
checkUuid, isHandle, isTitleTag, isItemClass, isItemType, isItemLayout,
hexToInt, minmax, checkIntTuple, formatInt, formatTimeStamp, formatTime,
simplified, splitVersionNumber, transferCase, fuzzyTime, numberToRoman,
jsonEncode, readTextFile, makeFileNameSafe, ensureFolder, sha256sum,
getGuiItem, NWConfigParser
)
@@ -137,6 +137,20 @@ def testBaseCommon_CheckHandle():
# END Test testBaseCommon_CheckHandle
@pytest.mark.base
def testBaseCommon_CheckUuid():
"""Test the checkUuid function.
"""
testUuid = "e2be99af-f9bf-4403-857a-c3d1ac25abea"
assert checkUuid("", None) is None
assert checkUuid("e2be99af-f9bf-4403-857a-c3d1ac25abe", None) is None
assert checkUuid("e2be99af-f9bf-qq03-857a-c3d1ac25abea", None) is None
assert checkUuid("e2be99af-f9bf-4403-857a-c3d1ac25abeaa", None) is None
assert checkUuid(testUuid, None) == testUuid
# END Test testBaseCommon_CheckUuid
@pytest.mark.base
def testBaseCommon_IsHandle():
"""Test the isHandle function.
+10 -3
View File
@@ -20,6 +20,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import os
import uuid
import pytest
from shutil import copyfile
@@ -264,10 +265,12 @@ def testCoreTools_DocSplitter(monkeypatch, mockGUI, fncDir, outDir, refDir, mock
@pytest.mark.core
def testCoreTools_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
def testCoreTools_NewMinimal(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
"""Create a new project from a project wizard dictionary. With
default setting, creating a Minimal project.
"""
monkeypatch.setattr("uuid.uuid4", lambda *a: uuid.UUID("d0f3fe10-c6e6-4310-8bfd-181eb4224eed"))
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")
@@ -294,10 +297,12 @@ def testCoreTools_NewMinimal(fncDir, outDir, refDir, mockGUI, mockRnd):
@pytest.mark.core
def testCoreTools_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
def testCoreTools_NewCustomA(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
"""Create a new project from a project wizard dictionary.
Custom type with chapters and scenes.
"""
monkeypatch.setattr("uuid.uuid4", lambda *a: uuid.UUID("d0f3fe10-c6e6-4310-8bfd-181eb4224eed"))
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")
@@ -330,10 +335,12 @@ def testCoreTools_NewCustomA(fncDir, outDir, refDir, mockGUI, mockRnd):
@pytest.mark.core
def testCoreTools_NewCustomB(fncDir, outDir, refDir, mockGUI, mockRnd):
def testCoreTools_NewCustomB(monkeypatch, fncDir, outDir, refDir, mockGUI, mockRnd):
"""Create a new project from a project wizard dictionary.
Custom type without chapters, but with scenes.
"""
monkeypatch.setattr("uuid.uuid4", lambda *a: uuid.UUID("d0f3fe10-c6e6-4310-8bfd-181eb4224eed"))
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")
+5 -1
View File
@@ -232,7 +232,7 @@ def testCoreProjectXML_ReadCurrent(monkeypatch, tstPaths, fncPath):
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
copyfile(outFile, tstFile)
assert cmpFiles(tstFile, xmlFile)
assert cmpFiles(tstFile, refFile)
# END Test testCoreProjectXML_ReadCurrent
@@ -369,6 +369,7 @@ def testCoreProjectXML_ReadLegacy10(tstPaths, fncPath, mockRnd):
# Save the project again, which should produce an identical project xml
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
xmlWriter = ProjectXMLWriter(fncPath)
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
testFile = tstPaths.outDir / "projectXML_ReadLegacy10.nwx"
compFile = tstPaths.refDir / "projectXML_ReadLegacy10.nwx"
@@ -510,6 +511,7 @@ def testCoreProjectXML_ReadLegacy11(tstPaths, fncPath, mockRnd):
# Save the project again, which should produce an identical project xml
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
xmlWriter = ProjectXMLWriter(fncPath)
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
testFile = tstPaths.outDir / "projectXML_ReadLegacy11.nwx"
compFile = tstPaths.refDir / "projectXML_ReadLegacy11.nwx"
@@ -654,6 +656,7 @@ def testCoreProjectXML_ReadLegacy12(tstPaths, fncPath, mockRnd):
# Save the project again, which should produce an identical project xml
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
xmlWriter = ProjectXMLWriter(fncPath)
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
testFile = tstPaths.outDir / "projectXML_ReadLegacy12.nwx"
compFile = tstPaths.refDir / "projectXML_ReadLegacy12.nwx"
@@ -798,6 +801,7 @@ def testCoreProjectXML_ReadLegacy13(tstPaths, fncPath, mockRnd):
# Save the project again, which should produce an identical project xml
timeStamp = int(datetime.fromisoformat(xmlReader.timeStamp).timestamp())
xmlWriter = ProjectXMLWriter(fncPath)
data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
assert xmlWriter.write(data, packedContent, timeStamp, 1000) is True
testFile = tstPaths.outDir / "projectXML_ReadLegacy13.nwx"
compFile = tstPaths.refDir / "projectXML_ReadLegacy13.nwx"
+1
View File
@@ -170,6 +170,7 @@ def buildTestProject(theObject, projPath):
theProject.storage.openProjectInPlace(theProject.projPath)
theProject.setDefaultStatusImport()
theProject.data.setUuid("d0f3fe10-c6e6-4310-8bfd-181eb4224eed")
theProject.data.setName("New Project")
theProject.data.setTitle("New Novel")
theProject.data.setAuthors("Jane Doe")