Track recent projects via UUID (#2218)

This commit is contained in:
Veronica Berglyd Olsen
2025-01-31 00:03:19 +01:00
committed by GitHub
4 changed files with 65 additions and 27 deletions
+28 -16
View File
@@ -32,6 +32,7 @@ import sys
from datetime import datetime
from pathlib import Path
from time import time
from typing import TYPE_CHECKING
from PyQt5.QtCore import (
PYQT_VERSION, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, QLibraryInfo,
@@ -47,6 +48,9 @@ from novelwriter.common import (
from novelwriter.constants import nwFiles, nwUnicode
from novelwriter.error import formatException, logException
if TYPE_CHECKING: # pragma: no cover
from novelwriter.core.projectdata import NWProjectData
logger = logging.getLogger(__name__)
@@ -845,29 +849,30 @@ class RecentProjects:
def __init__(self, config: Config) -> None:
self._conf = config
self._data = {}
self._data: dict[str, dict[str, str | int]] = {}
self._map: dict[str, str] = {}
return
def loadCache(self) -> bool:
"""Load the cache file for recent projects."""
self._data = {}
self._map = {}
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
if cacheFile.is_file():
try:
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
data = json.load(inFile)
for path, entry in data.items():
self._data[path] = {
"title": entry.get("title", ""),
"words": entry.get("words", 0),
"time": entry.get("time", 0),
}
puuid = str(entry.get("uuid", ""))
title = str(entry.get("title", ""))
words = checkInt(entry.get("words", 0), 0)
saved = checkInt(entry.get("time", 0), 0)
if path and title:
self._setEntry(puuid, path, title, words, saved)
except Exception:
logger.error("Could not load recent project cache")
logException()
return False
return True
def saveCache(self) -> bool:
@@ -882,7 +887,6 @@ class RecentProjects:
logger.error("Could not save recent project cache")
logException()
return False
return True
def listEntries(self) -> list[tuple[str, str, int, int]]:
@@ -892,14 +896,15 @@ class RecentProjects:
for k, e in self._data.items()
]
def update(self, path: str | Path, title: str, words: int, saved: float | int) -> None:
def update(self, path: str | Path, data: NWProjectData, saved: float | int) -> None:
"""Add or update recent cache information on a given project."""
self._data[str(path)] = {
"title": title,
"words": int(words),
"time": int(saved),
}
self.saveCache()
try:
if (remove := self._map.get(data.uuid)) and (remove != str(path)):
self.remove(remove)
self._setEntry(data.uuid, str(path), data.name, sum(data.currCounts), int(saved))
self.saveCache()
except Exception:
pass
return
def remove(self, path: str | Path) -> None:
@@ -909,6 +914,13 @@ class RecentProjects:
self.saveCache()
return
def _setEntry(self, puuid: str, path: str, title: str, words: int, saved: int) -> None:
"""Set an entry in the recent projects record."""
self._data[path] = {"uuid": puuid, "title": title, "words": words, "time": saved}
if puuid:
self._map[puuid] = path
return
class RecentPaths:
+2 -6
View File
@@ -353,9 +353,7 @@ class NWProject:
# Update recent projects
if storePath := self._storage.storagePath:
CONFIG.recentProjects.update(
storePath, self._data.name, sum(self._data.initCounts), time()
)
CONFIG.recentProjects.update(storePath, self._data, time())
# Check the project tree consistency
# This also handles any orphaned files found
@@ -421,9 +419,7 @@ class NWProject:
# Update recent projects
if storagePath := self._storage.storagePath:
CONFIG.recentProjects.update(
storagePath, self._data.name, sum(self._data.currCounts), saveTime
)
CONFIG.recentProjects.update(storagePath, self._data, saveTime)
SHARED.newStatusMessage(self.tr("Saved Project: {0}").format(self._data.name))
self.setProjectChanged(False)
+22 -3
View File
@@ -31,6 +31,7 @@ import pytest
from novelwriter import CONFIG
from novelwriter.config import Config, RecentPaths, RecentProjects
from novelwriter.constants import nwFiles
from novelwriter.core.project import NWProject
from tests.mocked import MockApp, causeOSError
from tests.tools import cmpFiles, writeFile
@@ -382,7 +383,7 @@ def testBaseConfig_Internal(monkeypatch, fncPath):
@pytest.mark.base
def testBaseConfig_RecentCache(monkeypatch, tstPaths):
def testBaseConfig_RecentCache(monkeypatch, tstPaths, nwGUI):
"""Test recent cache file."""
cacheFile = tstPaths.cnfDir / nwFiles.RECENT_FILE
recent = RecentProjects(CONFIG)
@@ -396,8 +397,18 @@ def testBaseConfig_RecentCache(monkeypatch, tstPaths):
pathOne = tstPaths.cnfDir / "projPathOne" / nwFiles.PROJ_FILE
pathTwo = tstPaths.cnfDir / "projPathTwo" / nwFiles.PROJ_FILE
recent.update(pathOne, "Proj One", 100, 1600002000)
recent.update(pathTwo, "Proj Two", 200, 1600005600)
prjOne = NWProject()
prjTwo = NWProject()
prjOne.data.setUuid(None)
prjTwo.data.setUuid(None)
prjOne.data.setName("Proj One")
prjTwo.data.setName("Proj Two")
prjOne.data.setCurrCounts(100, 0)
prjTwo.data.setCurrCounts(200, 0)
recent.update(pathOne, prjOne.data, 1600002000)
recent.update(pathTwo, prjTwo.data, 1600005600)
assert recent.listEntries() == [
(str(pathOne), "Proj One", 100, 1600002000),
(str(pathTwo), "Proj Two", 200, 1600005600),
@@ -429,6 +440,14 @@ def testBaseConfig_RecentCache(monkeypatch, tstPaths):
(str(pathTwo), "Proj Two", 200, 1600005600),
]
# Pass Invalid
recent.update(None, None, 0) # type: ignore
recent.update(None, None, 0) # type: ignore
assert recent.listEntries() == [
(str(pathOne), "Proj One", 100, 1600002000),
(str(pathTwo), "Proj Two", 200, 1600005600),
]
# Remove Non-Existent Entry
recent.remove("stuff")
assert recent.listEntries() == [
+13 -2
View File
@@ -31,6 +31,7 @@ from pytestqt.qtbot import QtBot
from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwFiles
from novelwriter.core.projectdata import NWProjectData
from novelwriter.enum import nwItemClass
from novelwriter.tools.welcome import GuiWelcome
from novelwriter.types import QtMouseLeft
@@ -72,8 +73,18 @@ def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
monkeypatch.setattr(QMenu, "exec", lambda *a: None)
monkeypatch.setattr(QMenu, "setParent", lambda *a: None)
CONFIG.recentProjects.update("/stuff/project_one", "Project One", 12345, 1690000000)
CONFIG.recentProjects.update("/stuff/project_two", "Project Two", 54321, 1700000000)
data1 = NWProjectData(SHARED.project)
data2 = NWProjectData(SHARED.project)
data1.setUuid(None)
data2.setUuid(None)
data1.setName("Project One")
data2.setName("Project Two")
data1.setCurrCounts(12345, 0)
data2.setCurrCounts(54321, 0)
CONFIG.recentProjects.update("/stuff/project_one", data1, 1690000000)
CONFIG.recentProjects.update("/stuff/project_two", data2, 1700000000)
dateOne = CONFIG.localDate(datetime.fromtimestamp(1700000000))
dateTwo = CONFIG.localDate(datetime.fromtimestamp(1690000000))