Feature add query command (#970)
* Added query_command and example * Refactored QUERY_OPTIONS, added query_command, refactored verbose, #930, #931 * Added query options to debug-dump, #966 * Refactored query, #602 * Added precedence test for --load-config * Refactored handling of query options * Refactored export_photo * Removed extraneous print * Updated API_README * Updated examples
This commit is contained in:
67
examples/cli_example_1.py
Normal file
67
examples/cli_example_1.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Sample query command for osxphotos
|
||||
|
||||
This shows how simple it is to create a command line tool using osxphotos to process your photos.
|
||||
|
||||
Using the @query_command decorator turns your function to a full-fledged command line app that
|
||||
can be run via `osxphotos run cli_example_1.py` or `python cli_example_1.py` if you have pip installed osxphotos.
|
||||
|
||||
Using this decorator makes it very easy to create a quick command line tool that can operate on
|
||||
a subset of your photos. Additionally, writing a command in this way makes it easy to later
|
||||
incorporate the command into osxphotos as a full-fledged command.
|
||||
|
||||
The decorator will add all the query options available in `osxphotos query` as command line options
|
||||
as well as the following options:
|
||||
--verbose
|
||||
--timestamp
|
||||
--theme
|
||||
--db
|
||||
--debug (hidden, won't show in help)
|
||||
|
||||
The decorated function will perform the query and pass the list of filtered PhotoInfo objects
|
||||
to your function. You can then do whatever you want with the photos.
|
||||
|
||||
For example, to run the command on only selected photos:
|
||||
|
||||
osxphotos run cli_example_1.py --selected
|
||||
|
||||
To run the command on all photos with the keyword "foo":
|
||||
|
||||
osxphotos run cli_example_1.py --keyword foo
|
||||
|
||||
For more advanced example, see `cli_example_2.py`
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.cli import query_command, verbose
|
||||
|
||||
|
||||
@query_command
|
||||
def example(photos: list[osxphotos.PhotoInfo], **kwargs):
|
||||
"""Sample query command for osxphotos. Prints out the filename and date of each photo.
|
||||
|
||||
Whatever text you put in the function's docstring here, will be used as the command's
|
||||
help text when run via `osxphotos run cli_example_1.py --help` or `python cli_example_1.py --help`
|
||||
"""
|
||||
|
||||
# verbose() will print to stdout if --verbose option is set
|
||||
# you can optionally provide a level (default is 1) to print only if --verbose is set to that level
|
||||
# for example: -VV or --verbose --verbose == level 2
|
||||
verbose(f"Found {len(photos)} photo(s)")
|
||||
verbose("This message will only be printed if verbose level 2 is set", level=2)
|
||||
|
||||
# do something with photos here
|
||||
for photo in photos:
|
||||
# photos is a list of PhotoInfo objects
|
||||
# see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo
|
||||
verbose(f"Processing {photo.original_filename}")
|
||||
print(f"{photo.original_filename} {photo.date}")
|
||||
...
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# call your function here
|
||||
# you do not need to pass any arguments to the function
|
||||
# as the decorator will handle parsing the command line arguments
|
||||
example()
|
||||
160
examples/cli_example_2.py
Normal file
160
examples/cli_example_2.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""Sample query command for osxphotos
|
||||
|
||||
This shows how simple it is to create a command line tool using osxphotos to process your photos.
|
||||
|
||||
Using the @query_command decorator turns your function to a full-fledged command line app that
|
||||
can be run via `osxphotos run cli_example_2.py` or `python cli_example_2.py` if you have pip installed osxphotos.
|
||||
|
||||
Using this decorator makes it very easy to create a quick command line tool that can operate on
|
||||
a subset of your photos. Additionally, writing a command in this way makes it easy to later
|
||||
incorporate the command into osxphotos as a full-fledged command.
|
||||
|
||||
The decorator will add all the query options available in `osxphotos query` as command line options
|
||||
as well as the following options:
|
||||
--verbose
|
||||
--timestamp
|
||||
--theme
|
||||
--db
|
||||
--debug (hidden, won't show in help)
|
||||
|
||||
The decorated function will perform the query and pass the list of filtered PhotoInfo objects
|
||||
to your function. You can then do whatever you want with the photos.
|
||||
|
||||
For example, to run the command on only selected photos:
|
||||
|
||||
osxphotos run cli_example_2.py --selected
|
||||
|
||||
To run the command on all photos with the keyword "foo":
|
||||
|
||||
osxphotos run cli_example_2.py --keyword foo
|
||||
|
||||
The following helper functions may be useful and can be imported from osxphotos.cli:
|
||||
|
||||
abort(message: str, exit_code: int = 1)
|
||||
Abort with error message and exit code
|
||||
echo(message: str)
|
||||
Print message to stdout using rich formatting
|
||||
echo_error(message: str)
|
||||
Print message to stderr using rich formatting
|
||||
logger: logging.Logger
|
||||
Python logger for osxphotos; for example, logger.debug("debug message")
|
||||
verbose(*args, level: int = 1)
|
||||
Print args to stdout if --verbose option is set
|
||||
query_command: decorator to create an osxphotos query command
|
||||
kvstore(name: str) -> SQLiteKVStore useful for storing state between runs
|
||||
|
||||
The verbose, echo, and echo_error functions use rich formatting to print messages to stdout and stderr.
|
||||
See https://github.com/Textualize/rich for more information on rich formatting.
|
||||
|
||||
In addition to standard rich formatting styles, the following styles will be defined
|
||||
(and can be changed using --theme):
|
||||
|
||||
[change]: something change
|
||||
[no_change]: indicate no change
|
||||
[count]: a count
|
||||
[error]: an error
|
||||
[filename]: a filename
|
||||
[filepath]: a filepath
|
||||
[num]: a number
|
||||
[time]: a time or date
|
||||
[tz]: a timezone
|
||||
[warning]: a warning
|
||||
[uuid]: a uuid
|
||||
|
||||
The tags should be closed with [/] to end the style. For example:
|
||||
|
||||
echo("[filename]foo[/] [time]bar[/]")
|
||||
|
||||
For simpler examples, see `cli_example_1.py`
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
import click
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.cli import (
|
||||
abort,
|
||||
echo,
|
||||
echo_error,
|
||||
kvstore,
|
||||
logger,
|
||||
query_command,
|
||||
verbose,
|
||||
)
|
||||
|
||||
|
||||
@query_command()
|
||||
@click.option(
|
||||
"--resume",
|
||||
is_flag=True,
|
||||
help="Resume processing from last run, do not reprocess photos",
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run", is_flag=True, help="Do a dry run, don't actually do anything"
|
||||
)
|
||||
def example(resume, dry_run, photos: list[osxphotos.PhotoInfo], **kwargs):
|
||||
"""Sample query command for osxphotos. Prints out the filename and date of each photo.
|
||||
|
||||
Whatever text you put in the function's docstring here, will be used as the command's
|
||||
help text when run via `osxphotos run cli_example_2.py --help` or `python cli_example_2.py --help`
|
||||
|
||||
The @query_command decorator returns a click.command so you can add additional options
|
||||
using standard click decorators. For example, the --resume and --dry-run options.
|
||||
For more information on click, see https://palletsprojects.com/p/click/.
|
||||
"""
|
||||
|
||||
# abort will print the message to stderr and exit with the given exit code
|
||||
if not photos:
|
||||
abort("Nothing to do!", 1)
|
||||
|
||||
# verbose() will print to stdout if --verbose option is set
|
||||
# you can optionally provide a level (default is 1) to print only if --verbose is set to that level
|
||||
# for example: -VV or --verbose --verbose == level 2
|
||||
verbose(f"Found [count]{len(photos)}[/] photos")
|
||||
verbose("This message will only be printed if verbose level 2 is set", level=2)
|
||||
|
||||
# the logger is a python logging.Logger object
|
||||
# debug messages will only be printed if --debug option is set
|
||||
logger.debug(f"{kwargs=}")
|
||||
|
||||
# kvstore() returns a SQLiteKVStore object for storing state between runs
|
||||
# this is basically a persistent dictionary that can be used to store state
|
||||
# see https://github.com/RhetTbull/sqlitekvstore for more information
|
||||
kv = kvstore("cli_example_2")
|
||||
verbose(f"Using key-value cache: {kv.path}")
|
||||
|
||||
# do something with photos here
|
||||
for photo in photos:
|
||||
# photos is a list of PhotoInfo objects
|
||||
# see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo
|
||||
if resume and photo.uuid in kv:
|
||||
echo(
|
||||
f"Skipping processed photo [filename]{photo.original_filename}[/] ([uuid]{photo.uuid}[/])"
|
||||
)
|
||||
continue
|
||||
|
||||
# store the uuid and current time in the kvstore
|
||||
# the key and value must be a type supported by SQLite: int, float, str, bytes, bool, None
|
||||
# if you need to store other values, you should serialize them to a string or bytes first
|
||||
# for example, using json.dumps() or pickle.dumps()
|
||||
kv[photo.uuid] = datetime.datetime.now().isoformat()
|
||||
echo(f"Processing [filename]{photo.original_filename}[/] [time]{photo.date}[/]")
|
||||
if not dry_run:
|
||||
# do something with the photo here
|
||||
echo(f"Doing something with [filename]{photo.original_filename}[/]")
|
||||
|
||||
# echo_error will print to stderr
|
||||
# if you add [warning] or [error], it will be formatted accordingly
|
||||
# and include an emoji to make the message stand out
|
||||
echo_error("[warning]This is a warning message!")
|
||||
echo_error("[error]This is an error message!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# call your function here
|
||||
# you do not need to pass any arguments to the function
|
||||
# as the decorator will handle parsing the command line arguments
|
||||
example()
|
||||
60
examples/cli_example_3.py
Normal file
60
examples/cli_example_3.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""Sample query command for osxphotos
|
||||
|
||||
This shows how simple it is to create a command line tool using osxphotos to process your photos.
|
||||
|
||||
Using the @selection_command decorator turns your function to a full-fledged command line app that
|
||||
can be run via `osxphotos run cli_example_1.py` or `python cli_example_1.py` if you have pip installed osxphotos.
|
||||
|
||||
Using this decorator makes it very easy to create a quick command line tool that can operate on
|
||||
a subset of your photos. Additionally, writing a command in this way makes it easy to later
|
||||
incorporate the command into osxphotos as a full-fledged command.
|
||||
|
||||
The decorator will add the following options to your command:
|
||||
--verbose
|
||||
--timestamp
|
||||
--theme
|
||||
--db
|
||||
--debug (hidden, won't show in help)
|
||||
|
||||
The decorated function will get the selected photos and pass the list of PhotoInfo objects
|
||||
to your function. You can then do whatever you want with the photos.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import osxphotos
|
||||
from osxphotos.cli import (
|
||||
selection_command,
|
||||
verbose,
|
||||
)
|
||||
|
||||
|
||||
@selection_command
|
||||
def example(photos: list[osxphotos.PhotoInfo], **kwargs):
|
||||
"""Sample command for osxphotos. Prints out the filename and date of each photo
|
||||
currently selected in Photos.app.
|
||||
|
||||
Whatever text you put in the function's docstring here, will be used as the command's
|
||||
help text when run via `osxphotos run cli_example_1.py --help` or `python cli_example_1.py --help`
|
||||
"""
|
||||
|
||||
# verbose() will print to stdout if --verbose option is set
|
||||
# you can optionally provide a level (default is 1) to print only if --verbose is set to that level
|
||||
# for example: -VV or --verbose --verbose == level 2
|
||||
verbose(f"Found {len(photos)} photo(s)")
|
||||
verbose("This message will only be printed if verbose level 2 is set", level=2)
|
||||
|
||||
# do something with photos here
|
||||
for photo in photos:
|
||||
# photos is a list of PhotoInfo objects
|
||||
# see: https://rhettbull.github.io/osxphotos/reference.html#osxphotos.PhotoInfo
|
||||
verbose(f"Processing {photo.original_filename}")
|
||||
print(f"{photo.original_filename} {photo.date}")
|
||||
...
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# call your function here
|
||||
# you do not need to pass any arguments to the function
|
||||
# as the decorator will handle parsing the command line arguments
|
||||
example()
|
||||
Reference in New Issue
Block a user