@@ -55,7 +55,7 @@ from osxphotos.photosalbum import PhotosAlbum
|
||||
from osxphotos.phototemplate import PhotoTemplate, RenderOptions
|
||||
from osxphotos.queryoptions import QueryOptions
|
||||
from osxphotos.uti import get_preferred_uti_extension
|
||||
from osxphotos.utils import format_sec_to_hhmmss, normalize_fs_path
|
||||
from osxphotos.utils import format_sec_to_hhmmss, normalize_fs_path, pluralize
|
||||
|
||||
from .click_rich_echo import (
|
||||
rich_click_echo,
|
||||
@@ -138,6 +138,13 @@ from .verbose import get_verbose_console, time_stamp, verbose_print
|
||||
help="If used with --update, ignores any previously exported files, even if missing from "
|
||||
"the export folder and only exports new files that haven't previously been exported.",
|
||||
)
|
||||
@click.option(
|
||||
"--limit",
|
||||
metavar="LIMIT",
|
||||
help="Export at most LIMIT photos. "
|
||||
"Useful for testing. Maybe used with --update to export incrementally.",
|
||||
type=int,
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
is_flag=True,
|
||||
@@ -730,6 +737,7 @@ def export(
|
||||
keyword_template,
|
||||
keyword,
|
||||
label,
|
||||
limit,
|
||||
live,
|
||||
load_config,
|
||||
location,
|
||||
@@ -945,6 +953,7 @@ def export(
|
||||
keyword = cfg.keyword
|
||||
keyword_template = cfg.keyword_template
|
||||
label = cfg.label
|
||||
limit = cfg.limit
|
||||
live = cfg.live
|
||||
location = cfg.location
|
||||
max_size = cfg.max_size
|
||||
@@ -1383,8 +1392,7 @@ def export(
|
||||
|
||||
if photos:
|
||||
num_photos = len(photos)
|
||||
# TODO: photos or photo appears several times, pull into a separate function
|
||||
photo_str = "photos" if num_photos > 1 else "photo"
|
||||
photo_str = pluralize(num_photos, "photo", "photos")
|
||||
rich_echo(
|
||||
f"Exporting [num]{num_photos}[/num] {photo_str} to [filepath]{dest}[/]..."
|
||||
)
|
||||
@@ -1412,9 +1420,11 @@ def export(
|
||||
)
|
||||
|
||||
photo_num = 0
|
||||
num_exported = 0
|
||||
limit_str = f" (limit = [num]{limit}[/num])" if limit else ""
|
||||
with rich_progress(console=get_verbose_console(), mock=no_progress) as progress:
|
||||
task = progress.add_task(
|
||||
f"Exporting [num]{num_photos}[/] photos", total=num_photos
|
||||
f"Exporting [num]{num_photos}[/] photos{limit_str}", total=num_photos
|
||||
)
|
||||
for p in photos:
|
||||
photo_num += 1
|
||||
@@ -1579,7 +1589,17 @@ def export(
|
||||
|
||||
progress.advance(task)
|
||||
|
||||
photo_str_total = "photos" if len(photos) != 1 else "photo"
|
||||
# handle limit
|
||||
if export_results.exported:
|
||||
# if any photos were exported, increment num_exported used by limit
|
||||
# limit considers each PhotoInfo object as a single photo even if multiple files are exported
|
||||
num_exported += 1
|
||||
if limit and num_exported >= limit:
|
||||
# advance progress to end
|
||||
progress.advance(task, num_photos - photo_num)
|
||||
break
|
||||
|
||||
photo_str_total = pluralize(len(photos), "photo", "photos")
|
||||
if update or force_update:
|
||||
summary = (
|
||||
f"Processed: [num]{len(photos)}[/] {photo_str_total}, "
|
||||
@@ -1597,6 +1617,8 @@ def export(
|
||||
summary += f"error: [num]{len(results.error)}[/]"
|
||||
if touch_file:
|
||||
summary += f", touched date: [num]{len(results.touched)}[/]"
|
||||
if limit:
|
||||
summary += f", limit: [num]{num_exported}[/]/[num]{limit}[/] exported"
|
||||
rich_echo(summary)
|
||||
stop_time = time.perf_counter()
|
||||
rich_echo(f"Elapsed time: [time]{format_sec_to_hhmmss(stop_time-start_time)}")
|
||||
|
||||
@@ -516,7 +516,7 @@ def get_latest_version() -> Tuple[Optional[str], str]:
|
||||
return None, e
|
||||
|
||||
|
||||
def pluralize(count, singular, plural):
|
||||
def pluralize(count: Optional[int], singular: str, plural: str) -> str:
|
||||
"""Return singular or plural based on count"""
|
||||
return singular if count == 1 else plural
|
||||
|
||||
|
||||
@@ -12,8 +12,12 @@ from osxphotos.exiftool import _ExifToolProc
|
||||
|
||||
from .test_catalina_10_15_7 import UUID_DICT_LOCAL
|
||||
|
||||
# run timewarp tests (configured with --timewarp)
|
||||
TEST_TIMEWARP = False
|
||||
|
||||
# don't clean up crash logs (configured with --no-cleanup)
|
||||
NO_CLEANUP = False
|
||||
|
||||
|
||||
def get_os_version():
|
||||
import platform
|
||||
@@ -68,6 +72,12 @@ def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--timewarp", action="store_true", default=False, help="run --timewarp tests"
|
||||
)
|
||||
parser.addoption(
|
||||
"--no-cleanup",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="don't clean up crash logs after tests",
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
@@ -81,10 +91,15 @@ def pytest_configure(config):
|
||||
"markers", "timewarp: mark test as requiring --timewarp to run"
|
||||
)
|
||||
|
||||
# this is hacky but I can't figure out how to check config options in other fixtures
|
||||
if config.getoption("--timewarp"):
|
||||
global TEST_TIMEWARP
|
||||
TEST_TIMEWARP = True
|
||||
|
||||
if config.getoption("--no-cleanup"):
|
||||
global NO_CLEANUP
|
||||
NO_CLEANUP = True
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if not (config.getoption("--addalbum") and TEST_LIBRARY is not None):
|
||||
@@ -163,7 +178,7 @@ def delete_crash_logs():
|
||||
"""Delete left over crash logs from tests that were supposed to crash"""
|
||||
yield
|
||||
path = pathlib.Path(os.getcwd()) / "osxphotos_crash.log"
|
||||
if path.is_file():
|
||||
if path.is_file() and not NO_CLEANUP:
|
||||
path.unlink()
|
||||
|
||||
|
||||
|
||||
@@ -7715,3 +7715,60 @@ def test_export_added_in_last():
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "Exporting" in result.output
|
||||
|
||||
|
||||
def test_export_limit():
|
||||
"""test export --limit"""
|
||||
|
||||
# Use --added-before so test doesn't break if photos added in the future
|
||||
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
with runner.isolated_filesystem():
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
".",
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"--update",
|
||||
"--limit",
|
||||
"20",
|
||||
"--added-before",
|
||||
"2022-05-07",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "limit: 20/20 exported" in result.output
|
||||
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
".",
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"--update",
|
||||
"--limit",
|
||||
"20",
|
||||
"--added-before",
|
||||
"2022-05-07",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "limit: 5/20 exported" in result.output
|
||||
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
".",
|
||||
"--db",
|
||||
os.path.join(cwd, PHOTOS_DB_15_7),
|
||||
"--update",
|
||||
"--limit",
|
||||
"20",
|
||||
"--added-before",
|
||||
"2022-05-07",
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
assert "limit: 0/20 exported" in result.output
|
||||
|
||||
Reference in New Issue
Block a user