Added today to template system, closes #167

This commit is contained in:
Rhet Turnbull
2020-06-21 21:58:18 -07:00
parent a8e996e660
commit c8d76a89e4
5 changed files with 185 additions and 9 deletions

View File

@@ -428,6 +428,34 @@ Substitution Description
{modified.hour} 2-digit hour of the file modification time
{modified.min} 2-digit minute of the file modification time
{modified.sec} 2-digit second of the file modification time
{today.date} Current date in iso format, e.g.
'2020-03-22'
{today.year} 4-digit year of current date
{today.yy} 2-digit year of current date
{today.mm} 2-digit month of the current date (zero
padded)
{today.month} Month name in user's locale of the current
date
{today.mon} Month abbreviation in the user's locale of
the current date
{today.dd} 2-digit day of the month (zero padded) of
current date
{today.dow} Day of week in user's locale of the current
date
{today.doy} 3-digit day of year (e.g Julian day) of
current date, starting from 1 (zero padded)
{today.hour} 2-digit hour of the current date
{today.min} 2-digit minute of the current date
{today.sec} 2-digit second of the current date
{today.strftime} Apply strftime template to current
date/time. Should be used in form
{today.strftime,TEMPLATE} where TEMPLATE is
a valid strftime template, e.g.
{today.strftime,%Y-%U} would result in year-
week number of year: '2020-23'. If used with
no template will return null value. See
https://strftime.org/ for help on strftime
templates.
{place.name} Place name from the photo's reverse
geolocation data, as displayed in Photos
{place.country_code} The ISO country code from the photo's
@@ -1461,7 +1489,6 @@ Example: find your "best" photo of food
### Template Substitutions
The following substitutions are availabe for use with `PhotoInfo.render_template()`
| Substitution | Description |
|--------------|-------------|
|{name}|Current filename of the photo|
@@ -1492,6 +1519,19 @@ The following substitutions are availabe for use with `PhotoInfo.render_template
|{modified.hour}|2-digit hour of the file modification time|
|{modified.min}|2-digit minute of the file modification time|
|{modified.sec}|2-digit second of the file modification time|
|{today.date}|Current date in iso format, e.g. '2020-03-22'|
|{today.year}|4-digit year of current date|
|{today.yy}|2-digit year of current date|
|{today.mm}|2-digit month of the current date (zero padded)|
|{today.month}|Month name in user's locale of the current date|
|{today.mon}|Month abbreviation in the user's locale of the current date|
|{today.dd}|2-digit day of the month (zero padded) of current date|
|{today.dow}|Day of week in user's locale of the current date|
|{today.doy}|3-digit day of year (e.g Julian day) of current date, starting from 1 (zero padded)|
|{today.hour}|2-digit hour of the current date|
|{today.min}|2-digit minute of the current date|
|{today.sec}|2-digit second of the current date|
|{today.strftime}|Apply strftime template to current date/time. Should be used in form {today.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. {today.strftime,%Y-%U} would result in year-week number of year: '2020-23'. If used with no template will return null value. See https://strftime.org/ for help on strftime templates.|
|{place.name}|Place name from the photo's reverse geolocation data, as displayed in Photos|
|{place.country_code}|The ISO country code from the photo's reverse geolocation data|
|{place.name.country}|Country name from the photo's reverse geolocation data|

View File

@@ -3,14 +3,9 @@ import logging
from ._version import __version__
from .photoinfo import PhotoInfo
from .photosdb import PhotosDB
from .utils import _set_debug, _debug, _get_logger
from .phototemplate import PhotoTemplate
from .utils import _debug, _get_logger, _set_debug
# TODO: find edited photos: see https://github.com/orangeturtle739/photos-export/blob/master/extract_photos.py
# TODO: Add test for imageTimeZoneOffsetSeconds = None
# TODO: Fix command line so multiple --keyword, etc. are AND (instead of OR as they are in .photos())
# Or fix the help text to match behavior
# TODO: Add test for __str__ and to_json
# TODO: fix docstrings
# TODO: Add special albums and magic albums
# TODO: cleanup os.path and pathlib code (import pathlib and also from pathlib import Path)

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.29.27"
__version__ = "0.29.28"

View File

@@ -9,6 +9,7 @@
# 4. Couldn't figure out how to do #1 and #2 with str.format()
#
# This code isn't elegant but it seems to work well. PRs gladly accepted.
import datetime
import locale
import os
import re
@@ -59,6 +60,23 @@ TEMPLATE_SUBSTITUTIONS = {
# + "{modified.strftime,%Y-%U} would result in year-week number of year: '2020-23'. "
# + "If used with no template will return null value. "
# + "See https://strftime.org/ for help on strftime templates.",
"{today.date}": "Current date in iso format, e.g. '2020-03-22'",
"{today.year}": "4-digit year of current date",
"{today.yy}": "2-digit year of current date",
"{today.mm}": "2-digit month of the current date (zero padded)",
"{today.month}": "Month name in user's locale of the current date",
"{today.mon}": "Month abbreviation in the user's locale of the current date",
"{today.dd}": "2-digit day of the month (zero padded) of current date",
"{today.dow}": "Day of week in user's locale of the current date",
"{today.doy}": "3-digit day of year (e.g Julian day) of current date, starting from 1 (zero padded)",
"{today.hour}": "2-digit hour of the current date",
"{today.min}": "2-digit minute of the current date",
"{today.sec}": "2-digit second of the current date",
"{today.strftime}": "Apply strftime template to current date/time. Should be used in form "
+ "{today.strftime,TEMPLATE} where TEMPLATE is a valid strftime template, e.g. "
+ "{today.strftime,%Y-%U} would result in year-week number of year: '2020-23'. "
+ "If used with no template will return null value. "
+ "See https://strftime.org/ for help on strftime templates.",
"{place.name}": "Place name from the photo's reverse geolocation data, as displayed in Photos",
"{place.country_code}": "The ISO country code from the photo's reverse geolocation data",
"{place.name.country}": "Country name from the photo's reverse geolocation data",
@@ -102,6 +120,10 @@ class PhotoTemplate:
"""
self.photo = photo
# holds value of current date/time for {today.x} fields
# gets initialized in get_template_value
self.today = None
def render(self, template, none_str="_", path_sep=None):
""" Render a filename or directory template
@@ -258,6 +280,10 @@ class PhotoTemplate:
ValueError if no rule exists for field.
"""
# initialize today with current date/time if needed
if self.today is None:
self.today = datetime.datetime.now()
# must be a valid keyword
if field == "name":
return pathlib.Path(self.photo.filename).stem
@@ -404,6 +430,51 @@ class PhotoTemplate:
# else:
# return None
if field == "today.date":
return DateTimeFormatter(self.today).date
if field == "today.year":
return DateTimeFormatter(self.today).year
if field == "today.yy":
return DateTimeFormatter(self.today).yy
if field == "today.mm":
return DateTimeFormatter(self.today).mm
if field == "today.month":
return DateTimeFormatter(self.today).month
if field == "today.mon":
return DateTimeFormatter(self.today).mon
if field == "today.dd":
return DateTimeFormatter(self.today).dd
if field == "today.dow":
return DateTimeFormatter(self.today).dow
if field == "today.doy":
return DateTimeFormatter(self.today).doy
if field == "today.hour":
return DateTimeFormatter(self.today).hour
if field == "today.min":
return DateTimeFormatter(self.today).min
if field == "today.sec":
return DateTimeFormatter(self.today).sec
if field == "today.strftime":
if default:
try:
return self.today.strftime(default)
except:
raise ValueError(f"Invalid strftime template: '{default}'")
else:
return None
if field == "place.name":
return self.photo.place.name if self.photo.place else None

View File

@@ -0,0 +1,70 @@
import datetime
import pytest
PHOTOS_DB_PLACES = (
"./tests/Test-Places-Catalina-10_15_1.photoslibrary/database/photos.db"
)
DATETIME_TODAY = datetime.datetime(2020, 6, 21, 13, 0, 0)
""" Used to patch osxphotos.phototemplate.TODAY for testing """
UUID_DICT = {
"place_dc": "128FB4C6-0B16-4E7D-9108-FB2E90DA1546",
"1_1_2": "1EB2B765-0765-43BA-A90C-0D0580E6172C",
"2_1_1": "D79B8D77-BFFC-460B-9312-034F2877D35B",
"0_2_0": "6191423D-8DB8-4D4C-92BE-9BBBA308AAC4",
"folder_album_1": "3DD2C897-F19E-4CA6-8C22-B027D5A71907",
"folder_album_no_folder": "D79B8D77-BFFC-460B-9312-034F2877D35B",
"mojave_album_1": "15uNd7%8RguTEgNPKHfTWw",
}
TODAY_VALUES = {
"{today.date}": "2020-06-21",
"{today.year}": "2020",
"{today.yy}": "20",
"{today.mm}": "06",
"{today.month}": "June",
"{today.mon}": "Jun",
"{today.dd}": "21",
"{today.dow}": "Sunday",
"{today.doy}": "173",
"{today.hour}": "13",
"{today.min}": "00",
"{today.sec}": "00",
}
def test_subst_today():
""" Test that substitutions are correct for {today.x}"""
import locale
import osxphotos
locale.setlocale(locale.LC_ALL, "en_US")
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB_PLACES)
photo = photosdb.photos(uuid=[UUID_DICT["place_dc"]])[0]
photo_template = osxphotos.PhotoTemplate(photo)
photo_template.today = DATETIME_TODAY
for template in TODAY_VALUES:
rendered, _ = photo_template.render(template)
assert rendered[0] == TODAY_VALUES[template]
def test_subst_strftime_today():
""" Test that strftime substitutions are correct for {today.strftime}"""
import locale
import osxphotos
locale.setlocale(locale.LC_ALL, "en_US")
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB_PLACES)
photo = photosdb.photos(uuid=[UUID_DICT["place_dc"]])[0]
photo_template = osxphotos.PhotoTemplate(photo)
photo_template.today = DATETIME_TODAY
rendered, unmatched = photo_template.render("{today.strftime,%Y-%m-%d-%H%M%S}")
assert rendered[0] == "2020-06-21-130000"
rendered, unmatched = photo.render_template("{today.strftime}")
assert rendered[0] == "_"