osxphotos/tests/test_cli_export_sidecar_template.py
Rhet Turnbull 93e1966607
Feature catch template error (#1141)
* Starting to implement catch_errors for sidecar_template

* Fixed error printing

* Fixed error printing

* Added tests for catch_errors
2023-08-03 06:33:17 -07:00

595 lines
19 KiB
Python

"""Test export with --sidecar-template (#1123)"""
import csv
import json
import os
import pathlib
import sqlite3
import pytest
from click.testing import CliRunner
from osxphotos.cli import export
PHOTOS_DB = "./tests/Test-10.15.7.photoslibrary"
PHOTO_UUID = "E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51" # wedding.jpg
SIDECAR_FILENAME = "wedding.jpg.txt"
SIDECAR_FILENAME_2 = "wedding.jpg.sidecar"
SIDECAR_DATA = """
Sidecar: wedding.jpg.txt
Photo: wedding.jpg
UUID: E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51
Rating: ★★★★★
"""
SIDECAR_DATA_2 = """
Sidecar: wedding.jpg.sidecar
Photo: wedding.jpg
UUID: E9BC5C36-7CD1-40A1-A72B-8B8FAC227D51
Rating: ★★★★★
"""
PHOTO_UUID_NO_KEYWORD = "4D521201-92AC-43E5-8F7C-59BC41C37A96" # IMG_1997.CR2
SIDECAR_FILENAME_NO_KEYWORD = "IMG_1997.CR2.txt"
def test_export_sidecar_template_1():
"""test basic export with --sidecar-template"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
assert sidecar_data == SIDECAR_DATA
def test_export_sidecar_template_option_case():
"""test basic export with --sidecar-template and option case insensitivity"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"None",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
assert sidecar_data == SIDECAR_DATA
def test_export_sidecar_template_strip_whitespace():
"""test basic export with --sidecar-template and STRIP_WHITESPACE = True"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"strip_whitespace",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
sidecar_expected = (
"\n".join(line.strip() for line in SIDECAR_DATA.splitlines()) + "\n"
)
assert sidecar_data == sidecar_expected
def test_export_sidecar_template_strip_lines():
"""test basic export with --sidecar-template and STRIP_LINES = True"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"strip_lines",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
sidecar_expected = "\n".join(
line for line in SIDECAR_DATA.splitlines() if line.strip()
)
assert sidecar_data == sidecar_expected
def test_export_sidecar_template_strip_lines_strip_whitespace():
"""test basic export with --sidecar-template and STRIP_LINES = True and STRIP_WHITESPACE = True"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"strip_whitespace,strip_lines",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
sidecar_expected = "\n".join(
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
)
assert sidecar_data == sidecar_expected
def test_export_sidecar_template_strip_lines_strip_whitespace_option_space():
"""test basic export with --sidecar-template and STRIP_LINES = True and STRIP_WHITESPACE = True with space in option"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"strip_whitespace, strip_lines",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
sidecar_expected = "\n".join(
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
)
assert sidecar_data == sidecar_expected
def test_export_sidecar_template_update_no():
"""test basic export with --sidecar-template and WRITE_SKIPPED = False, also test --cleanup"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
],
)
# run export again, should not update sidecar
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
"--update",
"--cleanup",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
sidecar_expected = "\n".join(
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
)
assert sidecar_data == SIDECAR_DATA
assert "Skipping existing sidecar file" in result.output
assert "Deleted: 0 files, 0 directories" in result.output
def test_export_sidecar_template_update_ues():
"""test basic export with --sidecar-template and WRITE_SKIPPED = True, also test --cleanup"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
],
)
# run export again, should not update sidecar
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"write_skipped",
"--update",
"--cleanup",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
sidecar_expected = "\n".join(
line.strip() for line in SIDECAR_DATA.splitlines() if line.strip()
)
assert sidecar_data == SIDECAR_DATA
assert "Skipping existing sidecar file" not in result.output
assert "Writing sidecar file" in result.output
assert "Deleted: 0 files, 0 directories" in result.output
def test_export_sidecar_template_report_csv():
"""test basic export with --sidecar-template --report to csv"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
"--report",
"report.csv",
],
)
assert result.exit_code == 0
# verify report output
report_file = pathlib.Path("report.csv")
assert report_file.exists()
csvreader = csv.DictReader(report_file.open())
assert "sidecar_user" in csvreader.fieldnames
found_sidecar = 0
for row in csvreader: # sourcery skip: no-loop-in-tests
# sidecar ends with .txt so verify report has sidecar_user = 1
if row["filename"].endswith(
".txt"
): # sourcery skip: no-conditionals-in-tests
assert str(row["sidecar_user"]) == "1"
found_sidecar += 1
else:
assert str(row["sidecar_user"]) == "0"
assert found_sidecar
def test_export_sidecar_template_report_json():
"""test basic export with --sidecar-template --report to json"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
"--report",
"report.json",
],
)
assert result.exit_code == 0
# read the json report output and verify it is correct
report_file = pathlib.Path("report.json")
assert report_file.exists()
report_data = json.loads(report_file.read_text())
assert "sidecar_user" in report_data[0]
found_sidecar = 0
for row in report_data: # sourcery skip: no-loop-in-tests
# sidecar ends with .txt so verify report has sidecar_user = 1
if row["filename"].endswith(
".txt"
): # sourcery skip: no-conditionals-in-tests
assert row["sidecar_user"]
found_sidecar += 1
else:
assert not row["sidecar_user"]
assert found_sidecar
def test_export_sidecar_template_report_db():
"""test basic export with --sidecar-template --report to sqlite db"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
"--report",
"report.db",
],
)
assert result.exit_code == 0
# read the report sqlite db and verify it is correct
report_file = pathlib.Path("report.db")
assert report_file.exists()
conn = sqlite3.connect(report_file)
c = conn.cursor()
c.execute("SELECT filename, sidecar_user FROM report")
rows = c.fetchall()
found_sidecar = 0
for row in rows: # sourcery skip: no-loop-in-tests
# sidecar ends with .txt so verify report has sidecar_user = 1
if row[0].endswith(".txt"): # sourcery skip: no-conditionals-in-tests
assert row[1] == 1
found_sidecar += 1
else:
assert row[1] == 0
assert found_sidecar
def test_export_sidecar_template_multiple():
"""test export with multiple --sidecar-template options"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.sidecar",
"none",
],
)
assert result.exit_code == 0
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
assert sidecar_data == SIDECAR_DATA
sidecar_file = pathlib.Path(SIDECAR_FILENAME_2)
assert sidecar_file.exists()
sidecar_data = sidecar_file.read_text()
assert sidecar_data == SIDECAR_DATA_2
def test_export_sidecar_template_full_library():
"""test export with --sidecar-template option against full library (repeated calls to generate sidecar files))"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar.mako"),
"{filepath}.txt",
"none",
],
)
assert result.exit_code == 0
def test_export_sidecar_template_skip_zero():
"""test basic export with --sidecar-template with skip_zero option"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--uuid",
PHOTO_UUID_NO_KEYWORD,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar_zero.mako"),
"{filepath}.txt",
"strip_whitespace,strip_lines,skip_zero",
],
)
assert result.exit_code == 0
assert "Skipping empty sidecar file" in result.output
sidecar_file = pathlib.Path(SIDECAR_FILENAME)
assert sidecar_file.exists()
sidecar_file = pathlib.Path(SIDECAR_FILENAME_NO_KEYWORD)
assert not sidecar_file.exists()
def test_export_sidecar_template_error():
"""test basic export with --sidecar-template that generates an error"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar_bad.mako"),
"{filepath}.txt",
"none",
],
)
assert result.exit_code != 0
def test_export_sidecar_template_catch_error():
"""test basic export with --sidecar-template that catches an error"""
runner = CliRunner()
cwd = os.getcwd()
# pylint: disable=not-context-manager
with runner.isolated_filesystem():
result = runner.invoke(
export,
[
"--library",
os.path.join(cwd, PHOTOS_DB),
".",
"-V",
"--uuid",
PHOTO_UUID,
"--sidecar-template",
os.path.join(cwd, "tests", "custom_sidecar_bad.mako"),
"{filepath}.txt",
"catch_errors",
],
)
assert result.exit_code == 0