diff --git a/gramps_webapi/__main__.py b/gramps_webapi/__main__.py index b598a7ca..a6be0742 100644 --- a/gramps_webapi/__main__.py +++ b/gramps_webapi/__main__.py @@ -28,12 +28,11 @@ import click from whoosh.index import LockError - from .api.util import get_db_manager, get_search_indexer, list_trees -from .dbmanager import WebDbManager from .app import create_app from .auth import add_user, delete_user, fill_tree, user_db from .const import ENV_CONFIG_FILE, TREE_MULTI +from .dbmanager import WebDbManager logging.basicConfig() LOG = logging.getLogger("gramps_webapi") @@ -128,7 +127,11 @@ def search(ctx, tree): if app.config["TREE"] == TREE_MULTI: raise ValueError("`tree` is required when multi-tree support is enabled.") # needed for backwards compatibility! - dbmgr = WebDbManager(name=app.config["TREE"], create_if_missing=False) + dbmgr = WebDbManager( + name=app.config["TREE"], + create_if_missing=False, + ignore_lock=app.config["IGNORE_DB_LOCK"], + ) tree = dbmgr.dirname with app.app_context(): ctx.obj["db_manager"] = get_db_manager(tree=tree) diff --git a/gramps_webapi/api/resources/trees.py b/gramps_webapi/api/resources/trees.py index 70ca5b0c..b30d0ab4 100644 --- a/gramps_webapi/api/resources/trees.py +++ b/gramps_webapi/api/resources/trees.py @@ -50,7 +50,11 @@ def get_tree_details(tree_id: str) -> Dict[str, str]: """Get details about a tree.""" try: - dbmgr = WebDbManager(dirname=tree_id, create_if_missing=False) + dbmgr = WebDbManager( + dirname=tree_id, + create_if_missing=False, + ignore_lock=current_app.config["IGNORE_DB_LOCK"], + ) except ValueError: abort(404) usage = get_tree_usage(tree_id) or {} @@ -100,7 +104,7 @@ def post(self, args): require_permissions([PERM_ADD_TREE]) tree_id = str(uuid.uuid4()) backend = current_app.config["NEW_DB_BACKEND"] - dbmgr = WebDbManager( + WebDbManager( dirname=tree_id, name=args["name"], create_if_missing=True, @@ -153,7 +157,11 @@ def put(self, args, tree_id: str): require_permissions([PERM_EDIT_OTHER_TREE]) validate_tree_id(tree_id) try: - dbmgr = WebDbManager(dirname=tree_id, create_if_missing=False) + dbmgr = WebDbManager( + dirname=tree_id, + create_if_missing=False, + ignore_lock=current_app.config["IGNORE_DB_LOCK"], + ) except ValueError: abort(404) rv = {} diff --git a/gramps_webapi/api/util.py b/gramps_webapi/api/util.py index 78fd4a0e..d4b78bcb 100644 --- a/gramps_webapi/api/util.py +++ b/gramps_webapi/api/util.py @@ -37,17 +37,17 @@ from gramps.gen.const import GRAMPS_LOCALE from gramps.gen.db.base import DbReadBase from gramps.gen.db.dbconst import ( + CITATION_KEY, DBBACKEND, - KEY_TO_NAME_MAP, - PERSON_KEY, - FAMILY_KEY, - SOURCE_KEY, EVENT_KEY, + FAMILY_KEY, + KEY_TO_NAME_MAP, MEDIA_KEY, + NOTE_KEY, + PERSON_KEY, PLACE_KEY, REPOSITORY_KEY, - NOTE_KEY, - CITATION_KEY, + SOURCE_KEY, ) from gramps.gen.dbstate import DbState from gramps.gen.errors import HandleError @@ -254,6 +254,7 @@ def get_db_manager(tree: Optional[str]) -> WebDbManager: username=current_app.config["POSTGRES_USER"], password=current_app.config["POSTGRES_PASSWORD"], create_if_missing=False, + ignore_lock=current_app.config["IGNORE_DB_LOCK"], ) @@ -472,7 +473,11 @@ def get_tree_id(guid: str) -> str: # multi-tree support enabled but user has no tree ID: forbidden! abort_with_message(403, "Forbidden") # needed for backwards compatibility: single-tree mode but user without tree ID - dbmgr = WebDbManager(name=current_app.config["TREE"], create_if_missing=False) + dbmgr = WebDbManager( + name=current_app.config["TREE"], + create_if_missing=False, + ignore_lock=current_app.config["IGNORE_DB_LOCK"], + ) tree_id = dbmgr.dirname return tree_id diff --git a/gramps_webapi/app.py b/gramps_webapi/app.py index 8206a185..4d83eb46 100644 --- a/gramps_webapi/app.py +++ b/gramps_webapi/app.py @@ -105,7 +105,11 @@ def create_app(config: Optional[Dict[str, Any]] = None): if app.config["TREE"] != TREE_MULTI: # create database if missing (only in single-tree mode) - WebDbManager(name=app.config["TREE"], create_if_missing=True) + WebDbManager( + name=app.config["TREE"], + create_if_missing=True, + ignore_lock=app.config["IGNORE_DB_LOCK"], + ) if app.config["TREE"] == TREE_MULTI and not app.config["MEDIA_PREFIX_TREE"]: warnings.warn( diff --git a/gramps_webapi/config.py b/gramps_webapi/config.py index dafb95db..fb3af0c5 100644 --- a/gramps_webapi/config.py +++ b/gramps_webapi/config.py @@ -48,6 +48,7 @@ class DefaultConfig(object): POSTGRES_PASSWORD = None POSTGRES_HOST = "localhost" POSTGRES_PORT = "5432" + IGNORE_DB_LOCK = False CELERY_CONFIG: Dict[str, str] = {} MEDIA_BASE_DIR = "" MEDIA_PREFIX_TREE = False diff --git a/gramps_webapi/dbloader.py b/gramps_webapi/dbloader.py index d4df6f64..797a51d7 100644 --- a/gramps_webapi/dbloader.py +++ b/gramps_webapi/dbloader.py @@ -36,7 +36,6 @@ from gramps.gen.recentfiles import recent_files from gramps.gen.utils.config import get_researcher - _ = glocale.translation.gettext LOG = logging.getLogger(__name__) @@ -106,9 +105,17 @@ def read_file(self, filename, mode: str, username: str, password: str): # set readonly correctly again self.dbstate.db.readonly = mode == DBMODE_R - def open_activate(self, filename, mode, username=None, password=None): + def open_activate( + self, + filename, + mode, + username=None, + password=None, + ignore_lock: bool = False, + ): """Open and make a family tree active.""" - check_lock(dir_name=filename, mode=mode) + if not ignore_lock: + check_lock(dir_name=filename, mode=mode) self.read_file(filename, mode, username, password) # Attempt to figure out the database title title = get_title(filename) diff --git a/gramps_webapi/dbmanager.py b/gramps_webapi/dbmanager.py index f4a25104..cd26cd9c 100644 --- a/gramps_webapi/dbmanager.py +++ b/gramps_webapi/dbmanager.py @@ -25,14 +25,13 @@ import os import uuid - from typing import Optional, Tuple -from gramps.cli.clidbman import CLIDbManager, NAME_FILE +from gramps.cli.clidbman import NAME_FILE, CLIDbManager from gramps.cli.user import User from gramps.gen.config import config from gramps.gen.db.dbconst import DBBACKEND, DBLOCKFN, DBMODE_R, DBMODE_W -from gramps.gen.db.utils import make_database, get_dbid_from_path +from gramps.gen.db.utils import get_dbid_from_path, make_database from gramps.gen.dbstate import DbState from .dbloader import WebDbSessionManager @@ -51,6 +50,7 @@ def __init__( password: Optional[str] = None, create_if_missing: bool = True, create_backend: str = "sqlite", + ignore_lock: bool = False, ) -> None: """Initialize given a family tree name or subdirectory name (path).""" if dirname: @@ -67,6 +67,7 @@ def __init__( self.password = password self.create_if_missing = create_if_missing self.create_backend = create_backend + self.ignore_lock = ignore_lock self.path = self._get_path() self._check_backend() @@ -154,7 +155,11 @@ def break_lock(self) -> None: if os.path.exists(os.path.join(self.path, DBLOCKFN)): os.unlink(os.path.join(self.path, DBLOCKFN)) - def get_db(self, readonly: bool = True, force_unlock: bool = False) -> DbState: + def get_db( + self, + readonly: bool = True, + force_unlock: bool = False, + ) -> DbState: """Open the database and return a dbstate instance. If `readonly` is `True` (default), write operations will fail (note, @@ -170,7 +175,11 @@ def get_db(self, readonly: bool = True, force_unlock: bool = False) -> DbState: self.break_lock() mode = DBMODE_R if readonly else DBMODE_W smgr.open_activate( - self.path, mode=mode, username=self.username, password=self.password + self.path, + mode=mode, + username=self.username, + password=self.password, + ignore_lock=self.ignore_lock, ) return dbstate