Skip to content

Commit

Permalink
Added export_directory table
Browse files Browse the repository at this point in the history
  • Loading branch information
RhetTbull committed May 3, 2024
1 parent 568fffc commit 83c6993
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 36 deletions.
6 changes: 3 additions & 3 deletions osxphotos/cli/exportdb.py
Expand Up @@ -192,7 +192,7 @@
is_flag=True,
help="Run in dry-run mode (don't actually update files); for example, use with --update-signatures or --migrate-photos-library.",
)
@click.argument("export_db", metavar="EXPORT_DATABASE", type=click.Path(exists=True))
@click.argument("export_db", metavar="EXPORT_DATABASE", type=click.Path())
def exportdb(
append,
check,
Expand Down Expand Up @@ -303,7 +303,7 @@ def exportdb(
)
sys.exit(1)

if not "4.3" <= create <= OSXPHOTOS_EXPORTDB_VERSION:
if not float("4.3") <= float(create) <= float(OSXPHOTOS_EXPORTDB_VERSION):
rich_echo_error(
f"[error]Error: invalid version number {create}: must be between >= 4.3, <= {OSXPHOTOS_EXPORTDB_VERSION}[/]"
)
Expand Down Expand Up @@ -590,7 +590,7 @@ def exportdb(
rich_echo(
dedent(
f"""
[warning]:warning-emoji: This command will update your export database ([filepath]{export_db}[/])
[warning]:warning-emoji: This command will update your export database ([filepath]{export_db}[/])
to use [filepath]{migrate_photos_library}[/] as the new source library.
The last library used was [filepath]{last_library}[/].
This will allow you to use the export database with the new library but it will
Expand Down
122 changes: 89 additions & 33 deletions osxphotos/export_db.py
@@ -1,5 +1,7 @@
""" Helper class for managing database used by PhotoExporter for tracking state of exports and updates """

# ZZZ replace all db version compares with floats

from __future__ import annotations

import datetime
Expand Down Expand Up @@ -40,7 +42,7 @@
"ExportDBTemp",
]

OSXPHOTOS_EXPORTDB_VERSION = "9.1"
OSXPHOTOS_EXPORTDB_VERSION = "10.0"
OSXPHOTOS_ABOUT_STRING = f"Created by osxphotos version {__version__} (https://github.com/RhetTbull/osxphotos) on {datetime.datetime.now()}"

# max retry attempts for methods which use tenacity.retry
Expand Down Expand Up @@ -128,6 +130,7 @@ def __init__(
self._conn = self._open_export_db(self._dbfile, version)
self._perform_db_maintenance(self._conn)
self._insert_run_info()
self._insert_export_dir()

@property
def path(self) -> str:
Expand Down Expand Up @@ -527,7 +530,7 @@ def _open_export_db(
conn = self._get_db_connection(dbfile)
self.was_created = False
version_info = self._get_database_version(conn)
if version_info[1] < OSXPHOTOS_EXPORTDB_VERSION:
if float(version_info[1]) < float(OSXPHOTOS_EXPORTDB_VERSION):
self._create_or_migrate_db_tables(conn)
self.was_upgraded = (version_info[1], OSXPHOTOS_EXPORTDB_VERSION)
else:
Expand Down Expand Up @@ -578,16 +581,18 @@ def _create_or_migrate_db_tables(
except Exception as e:
version_info = (__version__, "4.3")

version = version or OSXPHOTOS_EXPORTDB_VERSION
if version < "4.3":
max_version = float(OSXPHOTOS_EXPORTDB_VERSION)
version = float(version) if version else max_version
current_version = float(version_info[1])
if version < float("4.3"):
raise ValueError(
f"Requested database version {version} is older than minimum supported version 4.3"
)
if version > OSXPHOTOS_EXPORTDB_VERSION:
if version > max_version:
raise ValueError(
f"Requested database version {version} is newer than maximum supported version {OSXPHOTOS_EXPORTDB_VERSION}"
)
if version_info[1] > version:
if current_version > version:
raise ValueError(
f"Database version {version_info[1]} is newer than requested version {version}"
)
Expand All @@ -597,7 +602,7 @@ def _create_or_migrate_db_tables(
""" CREATE TABLE IF NOT EXISTS version (
id INTEGER PRIMARY KEY,
osxphotos TEXT,
exportdb TEXT
exportdb TEXT
); """,
""" CREATE TABLE IF NOT EXISTS about (
id INTEGER PRIMARY KEY,
Expand All @@ -621,17 +626,17 @@ def _create_or_migrate_db_tables(
python_path TEXT,
script_name TEXT,
args TEXT,
cwd TEXT
cwd TEXT
); """,
""" CREATE TABLE IF NOT EXISTS info (
id INTEGER PRIMARY KEY,
uuid text NOT NULL,
json_info JSON
json_info JSON
); """,
""" CREATE TABLE IF NOT EXISTS exifdata (
id INTEGER PRIMARY KEY,
filepath_normalized TEXT NOT NULL,
json_exifdata JSON
json_exifdata JSON
); """,
""" CREATE TABLE IF NOT EXISTS edited (
id INTEGER PRIMARY KEY,
Expand Down Expand Up @@ -668,6 +673,7 @@ def _create_or_migrate_db_tables(
""" CREATE UNIQUE INDEX IF NOT EXISTS idx_sidecar_filename on sidecar (filepath_normalized);""",
""" CREATE UNIQUE INDEX IF NOT EXISTS idx_detected_text on detected_text (uuid);""",
]

# create the tables if needed
with self.lock:
c = conn.cursor()
Expand All @@ -681,36 +687,40 @@ def _create_or_migrate_db_tables(
conn.commit()

# perform needed migrations
if version_info[1] < "4.3":
# compare float(str) to avoid any issues with comparing float(str) to float
if current_version < float("4.3"):
self._migrate_normalized_filepath(conn)

if version_info[1] < "5.0" and version >= "5.0":
if current_version < float("5.0") and version >= float("5.0"):
self._migrate_4_3_to_5_0(conn)

if version_info[1] < "6.0" and version >= "6.0":
if current_version < float("6.0") and version >= float("6.0"):
# create export_data table
self._migrate_5_0_to_6_0(conn)

if version_info[1] < "7.0" and version >= "7.0":
if current_version < float("7.0") and version >= float("7.0"):
# create report_data table
self._migrate_6_0_to_7_0(conn)

if version_info[1] < "7.1" and version >= "7.1":
if current_version < float("7.1") and version >= float("7.1"):
# add timestamp to export_data
self._migrate_7_0_to_7_1(conn)

if version_info[1] < "8.0" and version >= "8.0":
if current_version < float("8.0") and version >= float("8.0"):
# add error to export_data
self._migrate_7_1_to_8_0(conn)

if version_info[1] < "9.0" and version >= "9.0":
if current_version < float("9.0") and version >= float("9.0"):
# add history table
self._migrate_8_0_to_9_0(conn)

if version_info[1] < "9.1" and version >= "9.1":
if current_version < float("9.1") and version >= float("9.1"):
# Add history index
self._migrate_9_0_to_9_1(conn)

if current_version < float("10.0") and version >= float("10.0"):
self._migrate_9_1_to_10_0(conn)

with self.lock:
conn.execute("VACUUM;")
conn.commit()
Expand All @@ -722,6 +732,7 @@ def __del__(self):

@retry(stop=stop_after_attempt(MAX_RETRY_ATTEMPTS))
def _insert_run_info(self):
"""Insert run info into runs table"""
dt = datetime.datetime.now(datetime.timezone.utc).isoformat()
python_path = sys.executable
cmd = sys.argv[0]
Expand All @@ -736,6 +747,34 @@ def _insert_run_info(self):
)
conn.commit()

@retry(stop=stop_after_attempt(MAX_RETRY_ATTEMPTS))
def _insert_export_dir(self):
"""Insert export_directory info into export_directory table"""

if float(self.version) < float("10.0"):
return

with self.lock:
conn = self.connection
c = conn.cursor()
c.execute(
"""
INSERT INTO export_directory (export_directory)
SELECT ?
WHERE ? <> (
SELECT export_directory
FROM export_directory
ORDER BY rowid DESC
LIMIT 1
) OR NOT EXISTS (
SELECT 1 FROM export_directory
)
""",
(self._path, self._path),
)

conn.commit()

def _relative_filepath(self, filepath: pathlib.Path | str) -> str:
"""return filepath relative to self._path"""
return str(pathlib.Path(filepath).relative_to(self._path))
Expand Down Expand Up @@ -829,30 +868,30 @@ def _migrate_5_0_to_6_0(self, conn: sqlite3.Connection):
""" INSERT INTO export_data (filepath_normalized, filepath, uuid) SELECT filepath_normalized, filepath, uuid FROM files;""",
)
c.execute(
""" UPDATE export_data
SET (src_mode, src_size, src_mtime) =
(SELECT mode, size, mtime
FROM edited
""" UPDATE export_data
SET (src_mode, src_size, src_mtime) =
(SELECT mode, size, mtime
FROM edited
WHERE export_data.filepath_normalized = edited.filepath_normalized);
""",
)
c.execute(
""" UPDATE export_data
SET (dest_mode, dest_size, dest_mtime) =
(SELECT orig_mode, orig_size, orig_mtime
FROM files
""" UPDATE export_data
SET (dest_mode, dest_size, dest_mtime) =
(SELECT orig_mode, orig_size, orig_mtime
FROM files
WHERE export_data.filepath_normalized = files.filepath_normalized);
""",
)
c.execute(
""" UPDATE export_data SET digest =
(SELECT metadata FROM files
""" UPDATE export_data SET digest =
(SELECT metadata FROM files
WHERE files.filepath_normalized = export_data.filepath_normalized
); """
)
c.execute(
""" UPDATE export_data SET exifdata =
(SELECT json_exifdata FROM exifdata
""" UPDATE export_data SET exifdata =
(SELECT json_exifdata FROM exifdata
WHERE exifdata.filepath_normalized = export_data.filepath_normalized
); """
)
Expand All @@ -862,7 +901,7 @@ def _migrate_5_0_to_6_0(self, conn: sqlite3.Connection):
""" CREATE TABLE IF NOT EXISTS config (
id INTEGER PRIMARY KEY,
datetime TEXT,
config TEXT
config TEXT
); """
)

Expand Down Expand Up @@ -1023,9 +1062,24 @@ def _migrate_9_0_to_9_1(self, conn: sqlite3.Connection):

conn.commit()

def _migrate_9_1_to_10_0(self, conn: sqlite3.Connection):
"""Add export_directory table"""
with self.lock:
c = conn.cursor()

c.execute(
""" CREATE TABLE IF NOT EXISTS export_directory (
id INTEGER PRIMARY KEY,
export_directory TEXT
);
"""
)

conn.commit()

def _perform_db_maintenance(self, conn: sqlite3.Connection):
"""Perform database maintenance"""
if self.version < "6.0":
if float(self.version) < float("6.0"):
return
with self.lock:
c = conn.cursor()
Expand Down Expand Up @@ -1084,6 +1138,7 @@ def __init__(

self._conn = self._open_export_db(self._dbfile, version=version)
self._insert_run_info()
self._insert_export_dir()

@retry(stop=stop_after_attempt(MAX_RETRY_ATTEMPTS))
def write_to_disk(self):
Expand Down Expand Up @@ -1156,7 +1211,7 @@ def _open_export_db(

self.was_created = False
version_info = self._get_database_version(dst)
if version_info[1] < OSXPHOTOS_EXPORTDB_VERSION:
if float(version_info[1]) < float(OSXPHOTOS_EXPORTDB_VERSION):
self._create_or_migrate_db_tables(dst)
self.was_upgraded = (version_info[1], OSXPHOTOS_EXPORTDB_VERSION)
else:
Expand Down Expand Up @@ -1204,6 +1259,7 @@ def __init__(self, version: str | None = None):

self._conn = self._open_export_db(self._dbfile, version)
self._insert_run_info()
self._insert_export_dir()

def _relative_filepath(self, filepath: pathlib.Path | str) -> str:
"""Overrides _relative_filepath to return a path for use in the temp db"""
Expand Down

0 comments on commit 83c6993

Please sign in to comment.