From e95c0967846106f6da2adaa0b85520df8b351bb0 Mon Sep 17 00:00:00 2001 From: Rhet Turnbull Date: Fri, 23 Jul 2021 05:57:07 -0700 Subject: [PATCH] Added {id} sequence number template, #154 --- README.md | 9 +++++++++ osxphotos/_version.py | 2 +- osxphotos/photosdb/photosdb.py | 7 ++++++- osxphotos/phototemplate.py | 17 +++++++++++++++++ tests/test_template.py | 10 ++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63b20c2a..2fde3555 100644 --- a/README.md +++ b/README.md @@ -1801,6 +1801,14 @@ Substitution Description unique to the photo, e.g. '128FB4C6-0B16-4E7D-9108-FB2E90DA1546' +{id} A unique number for the photo based on its + primary key in the Photos database. A + sequential integer, e.g. 1, 2, 3...etc. May be + formatted using a python string format code. + For example, to format as a 5-digit integer + and pad with zeros, use '{id:05d}' which + results in 00001, 00002, 00003...etc. + {comma} A comma: ',' {semicolon} A semicolon: ';' {questionmark} A question mark: '?' @@ -3649,6 +3657,7 @@ The following template field substitutions are availabe for use the templating s |{exif.camera_model}|Camera model from original photo's EXIF information as imported by Photos, e.g. 'iPhone 6s'| |{exif.lens_model}|Lens model from original photo's EXIF information as imported by Photos, e.g. 'iPhone 6s back camera 4.15mm f/2.2'| |{uuid}|Photo's internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. '128FB4C6-0B16-4E7D-9108-FB2E90DA1546'| +|{id}|A unique number for the photo based on its primary key in the Photos database. A sequential integer, e.g. 1, 2, 3...etc. May be formatted using a python string format code. For example, to format as a 5-digit integer and pad with zeros, use '{id:05d}' which results in 00001, 00002, 00003...etc. | |{comma}|A comma: ','| |{semicolon}|A semicolon: ';'| |{questionmark}|A question mark: '?'| diff --git a/osxphotos/_version.py b/osxphotos/_version.py index 1af624dd..1203f3ce 100644 --- a/osxphotos/_version.py +++ b/osxphotos/_version.py @@ -1,3 +1,3 @@ """ version info """ -__version__ = "0.42.65" +__version__ = "0.42.66" diff --git a/osxphotos/photosdb/photosdb.py b/osxphotos/photosdb/photosdb.py index e3483917..1c0b7805 100644 --- a/osxphotos/photosdb/photosdb.py +++ b/osxphotos/photosdb/photosdb.py @@ -1104,6 +1104,7 @@ class PhotosDB: # get info on special types self._dbphotos[uuid]["specialType"] = row[25] self._dbphotos[uuid]["masterModelID"] = row[26] + self._dbphotos[uuid]["pk"] = row[26] # same as masterModelID, to match Photos 5 self._dbphotos[uuid]["panorama"] = True if row[25] == 1 else False self._dbphotos[uuid]["slow_mo"] = True if row[25] == 2 else False self._dbphotos[uuid]["time_lapse"] = True if row[25] == 3 else False @@ -1921,7 +1922,8 @@ class PhotosDB: {asset_table}.ZVISIBILITYSTATE, {asset_table}.ZTRASHEDDATE, {asset_table}.ZSAVEDASSETTYPE, - {asset_table}.ZADDEDDATE + {asset_table}.ZADDEDDATE, + {asset_table}.Z_PK FROM {asset_table} JOIN ZADDITIONALASSETATTRIBUTES ON ZADDITIONALASSETATTRIBUTES.ZASSET = {asset_table}.Z_PK ORDER BY {asset_table}.ZUUID """ @@ -1970,6 +1972,7 @@ class PhotosDB: # 39 ZGENERICASSET.ZTRASHEDDATE -- date item placed in the trash or null if not in trash # 40 ZGENERICASSET.ZSAVEDASSETTYPE -- how item imported # 41 ZGENERICASSET.ZADDEDDATE -- date item added to the library + # 42 ZGENERICASSET.Z_PK -- primary key for row in c: uuid = row[0] @@ -2154,6 +2157,8 @@ class PhotosDB: except (ValueError, TypeError): info["added_date"] = datetime(1970, 1, 1) + info["pk"] = row[42] + # initialize import session info which will be filled in later # not every photo has an import session so initialize all records now info["import_session"] = None diff --git a/osxphotos/phototemplate.py b/osxphotos/phototemplate.py index f6ec4d67..1a4df823 100644 --- a/osxphotos/phototemplate.py +++ b/osxphotos/phototemplate.py @@ -127,6 +127,10 @@ TEMPLATE_SUBSTITUTIONS = { "{exif.camera_model}": "Camera model from original photo's EXIF information as imported by Photos, e.g. 'iPhone 6s'", "{exif.lens_model}": "Lens model from original photo's EXIF information as imported by Photos, e.g. 'iPhone 6s back camera 4.15mm f/2.2'", "{uuid}": "Photo's internal universally unique identifier (UUID) for the photo, a 36-character string unique to the photo, e.g. '128FB4C6-0B16-4E7D-9108-FB2E90DA1546'", + "{id}": "A unique number for the photo based on its primary key in the Photos database. " + + "A sequential integer, e.g. 1, 2, 3...etc. May be formatted using a python string format code. " + + "For example, to format as a 5-digit integer and pad with zeros, use '{id:05d}' which results in " + + "00001, 00002, 00003...etc. ", "{comma}": "A comma: ','", "{semicolon}": "A semicolon: ';'", "{questionmark}": "A question mark: '?'", @@ -294,6 +298,14 @@ class PhotoTemplateParser: return self.metamodel.model_from_str(template_statement) +def format_id_str(value, format_str): + """Format value based on format code in field in format id:02d""" + if not format_str: + return str(value) + format_str = "{0:" + f"{format_str}" + "}" + return format_str.format(value) + + class PhotoTemplate: """PhotoTemplate class to render a template string from a PhotoInfo object""" @@ -491,6 +503,7 @@ class PhotoTemplate: vals = self.get_template_value( field, default=default, + subfield=subfield, # delim=delim or self.inplace_sep, # path_sep=path_sep, ) @@ -645,6 +658,7 @@ class PhotoTemplate: self, field, default, + subfield=None, # bool_val=None, # delim=None, # path_sep=None, @@ -657,6 +671,7 @@ class PhotoTemplate: bool_val: True value if expression is boolean delim: delimiter for expand in place path_sep: path separator for fields that are path-like + subfield: subfield (value after : in field) Returns: The matching template value (which may be None). @@ -923,6 +938,8 @@ class PhotoTemplate: value = self.photo.exif_info.lens_model if self.photo.exif_info else None elif field == "uuid": value = self.photo.uuid + elif field.startswith("id"): + value = format_id_str(self.photo._info["pk"], subfield) elif field in PUNCTUATION: value = PUNCTUATION[field] elif field == "osxphotos_version": diff --git a/tests/test_template.py b/tests/test_template.py index 2d8e2714..626b75c9 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -1095,3 +1095,13 @@ def test_filepath(): with pytest.raises(ValueError): rendered, _ = template.render("{filepath.foo}", options) + + +def test_id(photosdb): + """Test {id} template""" + photo = photosdb.get_photo(UUID_MULTI_KEYWORDS) + rendered, _ = photo.render_template("{id}") + assert rendered[0] == "7" + + rendered, _ = photo.render_template("{id:03d}") + assert rendered[0] == "007"