Added --force-update, #621

This commit is contained in:
Rhet Turnbull
2022-02-12 21:01:16 -08:00
parent a2f329b8de
commit 30abdddaf3
15 changed files with 50 additions and 27 deletions

View File

@@ -1732,7 +1732,7 @@ Substitution Description
{lf} A line feed: '\n', alias for {newline} {lf} A line feed: '\n', alias for {newline}
{cr} A carriage return: '\r' {cr} A carriage return: '\r'
{crlf} a carriage return + line feed: '\r\n' {crlf} a carriage return + line feed: '\r\n'
{osxphotos_version} The osxphotos version, e.g. '0.45.9' {osxphotos_version} The osxphotos version, e.g. '0.45.10'
{osxphotos_cmd_line} The full command line used to run osxphotos {osxphotos_cmd_line} The full command line used to run osxphotos
The following substitutions may result in multiple values. Thus if specified for The following substitutions may result in multiple values. Thus if specified for
@@ -3636,7 +3636,7 @@ The following template field substitutions are availabe for use the templating s
|{lf}|A line feed: '\n', alias for {newline}| |{lf}|A line feed: '\n', alias for {newline}|
|{cr}|A carriage return: '\r'| |{cr}|A carriage return: '\r'|
|{crlf}|a carriage return + line feed: '\r\n'| |{crlf}|a carriage return + line feed: '\r\n'|
|{osxphotos_version}|The osxphotos version, e.g. '0.45.9'| |{osxphotos_version}|The osxphotos version, e.g. '0.45.10'|
|{osxphotos_cmd_line}|The full command line used to run osxphotos| |{osxphotos_cmd_line}|The full command line used to run osxphotos|
|{album}|Album(s) photo is contained in| |{album}|Album(s) photo is contained in|
|{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder| |{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder|

View File

@@ -1,4 +1,4 @@
# Sphinx build info version 1 # Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: a320d2e66b198895ef0b12b1f5934727 config: 5bcb39b8d7265fb2c5780a64f97662ef
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.45.9', VERSION: '0.45.10',
LANGUAGE: 'None', LANGUAGE: 'None',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.45.9 documentation</title> <title>osxphotos command line interface (CLI) &#8212; osxphotos 0.45.10 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; osxphotos 0.45.9 documentation</title> <title>Index &#8212; osxphotos 0.45.10 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.45.9 documentation</title> <title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.45.10 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos &#8212; osxphotos 0.45.9 documentation</title> <title>osxphotos &#8212; osxphotos 0.45.10 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -6,7 +6,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>osxphotos package &#8212; osxphotos 0.45.9 documentation</title> <title>osxphotos package &#8212; osxphotos 0.45.10 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />
<script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script> <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; osxphotos 0.45.9 documentation</title> <title>Search &#8212; osxphotos 0.45.10 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/alabaster.css" /> <link rel="stylesheet" type="text/css" href="_static/alabaster.css" />

View File

@@ -1,3 +1,3 @@
""" version info """ """ version info """
__version__ = "0.45.9" __version__ = "0.45.10"

View File

@@ -3164,7 +3164,7 @@ def export_photo_to_directory(
) )
if verbose: if verbose:
if update: if update or force_update:
for new in results.new: for new in results.new:
verbose_(f"Exported new file {new}") verbose_(f"Exported new file {new}")
for updated in results.updated: for updated in results.updated:

View File

@@ -1033,9 +1033,9 @@ class PhotoExporter:
# need to also check the photo's metadata to that in the database # need to also check the photo's metadata to that in the database
# and if anything changed, we need to update the file # and if anything changed, we need to update the file
# ony the hex digest of the metadata is stored in the database # ony the hex digest of the metadata is stored in the database
cmp_orig = hexdigest( photo_digest = hexdigest(self.photo.json())
self.photo.json() db_digest = export_db.get_metadata_for_file(dest_str)
) == export_db.get_metadata_for_file(dest_str) cmp_orig = photo_digest == db_digest
sig_cmp = cmp_touch if options.touch_file else cmp_orig sig_cmp = cmp_touch if options.touch_file else cmp_orig
@@ -1138,6 +1138,8 @@ class PhotoExporter:
) from e ) from e
json_info = self.photo.json() json_info = self.photo.json()
# don't set the metadata digest if not force_update so that future use of force_update catches metadata change
metadata_digest = hexdigest(json_info) if options.force_update else None
export_db.set_data( export_db.set_data(
filename=dest_str, filename=dest_str,
uuid=self.photo.uuid, uuid=self.photo.uuid,
@@ -1145,7 +1147,7 @@ class PhotoExporter:
converted_stat=converted_stat, converted_stat=converted_stat,
edited_stat=edited_stat, edited_stat=edited_stat,
info_json=json_info, info_json=json_info,
metadata=hexdigest(json_info), metadata=metadata_digest,
) )
return ExportResults( return ExportResults(

View File

@@ -1729,6 +1729,9 @@ class PhotoInfo:
return o.isoformat() return o.isoformat()
dict_data = self.asdict() dict_data = self.asdict()
for k, v in dict_data.items():
if v and isinstance(v, (list, tuple)) and not isinstance(v[0], dict):
dict_data[k] = sorted(v)
return json.dumps(dict_data, sort_keys=True, default=default) return json.dumps(dict_data, sort_keys=True, default=default)
def __eq__(self, other): def __eq__(self, other):

View File

@@ -211,10 +211,12 @@ class SearchInfo:
"""return list of text for a specified category ID""" """return list of text for a specified category ID"""
if self._db_searchinfo: if self._db_searchinfo:
content = "normalized_string" if self._normalized else "content_string" content = "normalized_string" if self._normalized else "content_string"
return [ return sorted(
rec[content] [
for rec in self._db_searchinfo rec[content]
if rec["category"] == category for rec in self._db_searchinfo
] if rec["category"] == category
]
)
else: else:
return [] return []

View File

@@ -4845,15 +4845,12 @@ def test_export_force_update():
in result.output in result.output
) )
# force update # force update must be run once to set the metadata digest info
# in practice, this means that first time user uses --force-update, most files will likely be re-exported
result = runner.invoke( result = runner.invoke(
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"] export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
) )
assert result.exit_code == 0 assert result.exit_code == 0
assert (
f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0"
in result.output
)
# update a file # update a file
dbpath = os.path.join(photos_db_path, "database/Photos.sqlite") dbpath = os.path.join(photos_db_path, "database/Photos.sqlite")
@@ -4869,7 +4866,7 @@ def test_export_force_update():
) )
conn.commit() conn.commit()
# run again to see if updated metadata forced update # run --force-update to see if updated metadata forced update
result = runner.invoke( result = runner.invoke(
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"] export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
) )
@@ -4879,6 +4876,25 @@ def test_export_force_update():
in result.output in result.output
) )
# update, nothing should export
result = runner.invoke(
export, [os.path.join(cwd, photos_db_path), ".", "--update"]
)
assert result.exit_code == 0
assert (
f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0"
in result.output
)
# run --force-update, nothing should export
result = runner.invoke(
export, [os.path.join(cwd, photos_db_path), ".", "--force-update"]
)
assert result.exit_code == 0
assert (
f"Processed: {PHOTOS_NOT_IN_TRASH_LEN_15_7} photos, exported: 0, updated: 0, skipped: {PHOTOS_NOT_IN_TRASH_LEN_15_7+PHOTOS_EDITED_15_7}, updated EXIF data: 0, missing: 3, error: 0"
in result.output
)
@pytest.mark.skipif( @pytest.mark.skipif(
"OSXPHOTOS_TEST_EXPORT" not in os.environ, "OSXPHOTOS_TEST_EXPORT" not in os.environ,