Added --limit, #592 (#685)

This commit is contained in:
Rhet Turnbull
2022-05-07 08:59:01 -07:00
committed by GitHub
parent 9fddbefa66
commit 35a4777ae4
4 changed files with 101 additions and 7 deletions

View File

@@ -55,7 +55,7 @@ from osxphotos.photosalbum import PhotosAlbum
from osxphotos.phototemplate import PhotoTemplate, RenderOptions from osxphotos.phototemplate import PhotoTemplate, RenderOptions
from osxphotos.queryoptions import QueryOptions from osxphotos.queryoptions import QueryOptions
from osxphotos.uti import get_preferred_uti_extension 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 ( from .click_rich_echo import (
rich_click_echo, 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 " 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.", "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( @click.option(
"--dry-run", "--dry-run",
is_flag=True, is_flag=True,
@@ -730,6 +737,7 @@ def export(
keyword_template, keyword_template,
keyword, keyword,
label, label,
limit,
live, live,
load_config, load_config,
location, location,
@@ -945,6 +953,7 @@ def export(
keyword = cfg.keyword keyword = cfg.keyword
keyword_template = cfg.keyword_template keyword_template = cfg.keyword_template
label = cfg.label label = cfg.label
limit = cfg.limit
live = cfg.live live = cfg.live
location = cfg.location location = cfg.location
max_size = cfg.max_size max_size = cfg.max_size
@@ -1383,8 +1392,7 @@ def export(
if photos: if photos:
num_photos = len(photos) num_photos = len(photos)
# TODO: photos or photo appears several times, pull into a separate function photo_str = pluralize(num_photos, "photo", "photos")
photo_str = "photos" if num_photos > 1 else "photo"
rich_echo( rich_echo(
f"Exporting [num]{num_photos}[/num] {photo_str} to [filepath]{dest}[/]..." f"Exporting [num]{num_photos}[/num] {photo_str} to [filepath]{dest}[/]..."
) )
@@ -1412,9 +1420,11 @@ def export(
) )
photo_num = 0 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: with rich_progress(console=get_verbose_console(), mock=no_progress) as progress:
task = progress.add_task( 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: for p in photos:
photo_num += 1 photo_num += 1
@@ -1579,7 +1589,17 @@ def export(
progress.advance(task) 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: if update or force_update:
summary = ( summary = (
f"Processed: [num]{len(photos)}[/] {photo_str_total}, " f"Processed: [num]{len(photos)}[/] {photo_str_total}, "
@@ -1597,6 +1617,8 @@ def export(
summary += f"error: [num]{len(results.error)}[/]" summary += f"error: [num]{len(results.error)}[/]"
if touch_file: if touch_file:
summary += f", touched date: [num]{len(results.touched)}[/]" summary += f", touched date: [num]{len(results.touched)}[/]"
if limit:
summary += f", limit: [num]{num_exported}[/]/[num]{limit}[/] exported"
rich_echo(summary) rich_echo(summary)
stop_time = time.perf_counter() stop_time = time.perf_counter()
rich_echo(f"Elapsed time: [time]{format_sec_to_hhmmss(stop_time-start_time)}") rich_echo(f"Elapsed time: [time]{format_sec_to_hhmmss(stop_time-start_time)}")

View File

@@ -516,7 +516,7 @@ def get_latest_version() -> Tuple[Optional[str], str]:
return None, e 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 or plural based on count"""
return singular if count == 1 else plural return singular if count == 1 else plural

View File

@@ -12,8 +12,12 @@ from osxphotos.exiftool import _ExifToolProc
from .test_catalina_10_15_7 import UUID_DICT_LOCAL from .test_catalina_10_15_7 import UUID_DICT_LOCAL
# run timewarp tests (configured with --timewarp)
TEST_TIMEWARP = False TEST_TIMEWARP = False
# don't clean up crash logs (configured with --no-cleanup)
NO_CLEANUP = False
def get_os_version(): def get_os_version():
import platform import platform
@@ -68,6 +72,12 @@ def pytest_addoption(parser):
parser.addoption( parser.addoption(
"--timewarp", action="store_true", default=False, help="run --timewarp tests" "--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): def pytest_configure(config):
@@ -81,10 +91,15 @@ def pytest_configure(config):
"markers", "timewarp: mark test as requiring --timewarp to run" "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"): if config.getoption("--timewarp"):
global TEST_TIMEWARP global TEST_TIMEWARP
TEST_TIMEWARP = True TEST_TIMEWARP = True
if config.getoption("--no-cleanup"):
global NO_CLEANUP
NO_CLEANUP = True
def pytest_collection_modifyitems(config, items): def pytest_collection_modifyitems(config, items):
if not (config.getoption("--addalbum") and TEST_LIBRARY is not None): 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""" """Delete left over crash logs from tests that were supposed to crash"""
yield yield
path = pathlib.Path(os.getcwd()) / "osxphotos_crash.log" path = pathlib.Path(os.getcwd()) / "osxphotos_crash.log"
if path.is_file(): if path.is_file() and not NO_CLEANUP:
path.unlink() path.unlink()

View File

@@ -7715,3 +7715,60 @@ def test_export_added_in_last():
) )
assert result.exit_code == 0 assert result.exit_code == 0
assert "Exporting" in result.output 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