Add betterhelp docstring.
This commit is contained in:
parent
84b35c3eaa
commit
94130e4803
1 changed files with 277 additions and 18 deletions
|
@ -3,6 +3,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from voussoirkit import betterhelp
|
||||||
from voussoirkit import interactive
|
from voussoirkit import interactive
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
from voussoirkit import spinal
|
from voussoirkit import spinal
|
||||||
|
@ -393,6 +394,258 @@ def tag_breplace_argparse(args):
|
||||||
tag.add_synonym(tag_name)
|
tag.add_synonym(tag_name)
|
||||||
photodb.commit()
|
photodb.commit()
|
||||||
|
|
||||||
|
DOCSTRING = '''
|
||||||
|
Etiquette CLI
|
||||||
|
=============
|
||||||
|
|
||||||
|
{add_tag}
|
||||||
|
|
||||||
|
{remove_tag}
|
||||||
|
|
||||||
|
{digest}
|
||||||
|
|
||||||
|
{easybake}
|
||||||
|
|
||||||
|
{export_symlinks}
|
||||||
|
|
||||||
|
{init}
|
||||||
|
|
||||||
|
{purge_deleted_files}
|
||||||
|
|
||||||
|
{purge_empty_albums}
|
||||||
|
|
||||||
|
{search}
|
||||||
|
|
||||||
|
{show_associated_directories}
|
||||||
|
|
||||||
|
{set_searchhidden}
|
||||||
|
|
||||||
|
{unset_searchhidden}
|
||||||
|
|
||||||
|
{tag_breplace}
|
||||||
|
|
||||||
|
At any time, you may add --silent, --quiet, --debug, --loud to change logging.
|
||||||
|
|
||||||
|
You can add --yes to avoid the "Commit?" prompt.
|
||||||
|
|
||||||
|
TO SEE DETAILS ON EACH COMMAND, RUN
|
||||||
|
> etiquette_cli.py <command> --help
|
||||||
|
'''
|
||||||
|
|
||||||
|
SUB_DOCSTRINGS = dict(
|
||||||
|
add_tag='''
|
||||||
|
add_tag:
|
||||||
|
Add a tag to files by a filename glob.
|
||||||
|
|
||||||
|
> etiquette_cli.py add_tag tag_name glob_pattern
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
remove_tag='''
|
||||||
|
remove_tag:
|
||||||
|
Remove a tag from files by a filename glob.
|
||||||
|
|
||||||
|
> etiquette_cli.py remove_tag tag_name glob_pattern
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
digest='''
|
||||||
|
digest:
|
||||||
|
Digest a directory, adding new files as Photos into the database.
|
||||||
|
|
||||||
|
> etiquette_cli.py digest directory <flags>
|
||||||
|
|
||||||
|
flags:
|
||||||
|
--no_albums:
|
||||||
|
Do not create any albums the directories. By default, albums are created
|
||||||
|
and nested to match the directory structure.
|
||||||
|
|
||||||
|
--ratelimit X:
|
||||||
|
Limit the ingest of new Photos to only one per X seconds. This can be
|
||||||
|
used to reduce system load or to make sure that two photos don't get the
|
||||||
|
same `created` timestamp.
|
||||||
|
|
||||||
|
--no_recurse:
|
||||||
|
Do not recurse into subdirectories. Only create Photos from files in
|
||||||
|
the current directory.
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
easybake='''
|
||||||
|
easybake:
|
||||||
|
Create and manipulate tags by easybake strings.
|
||||||
|
|
||||||
|
> etiquette_cli.py easybake eb_string
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
export_symlinks='''
|
||||||
|
export_symlinks:
|
||||||
|
Search for photos or albums, then create symlinks pointing to the results.
|
||||||
|
|
||||||
|
THIS IS STILL A BIT EXPERIMENTAL.
|
||||||
|
This can be used to gather up search results for the purpose of further
|
||||||
|
uploading, transfering, etc. with other applications.
|
||||||
|
Symlinks point to files (if result is a photo) or directories (if result is
|
||||||
|
an album with an associated directory).
|
||||||
|
Albums are limited to only one associated directory since the output
|
||||||
|
symlink can't point to two places at once.
|
||||||
|
|
||||||
|
> etiquette_cli.py export_symlinks --destination directory --search searchargs
|
||||||
|
> etiquette_cli.py export_symlinks --destination directory --album-search searchargs
|
||||||
|
|
||||||
|
flags:
|
||||||
|
--destination:
|
||||||
|
A path to a directory into which the symlinks will be placed.
|
||||||
|
|
||||||
|
--dry:
|
||||||
|
Print the results without actually creating the symlinks.
|
||||||
|
|
||||||
|
--prune:
|
||||||
|
In the destination directory, any existing symlinks whose target no
|
||||||
|
longer exists will be deleted.
|
||||||
|
|
||||||
|
See search --help for more info about searchargs.
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
init='''
|
||||||
|
init:
|
||||||
|
Create a new Etiquette database in the current directory.
|
||||||
|
|
||||||
|
> etiquette_cli.py init
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
purge_deleted_files='''
|
||||||
|
purge_deleted_files:
|
||||||
|
Delete any Photo objects whose file no longer exists on disk.
|
||||||
|
|
||||||
|
> etiquette_cli.py purge_deleted_files
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
purge_empty_albums='''
|
||||||
|
purge_empty_albums:
|
||||||
|
Delete any albums which have no child albums or photos.
|
||||||
|
|
||||||
|
Consider running purge_deleted_files first, so that albums containing
|
||||||
|
deleted files will get cleared out and then caught by this function.
|
||||||
|
|
||||||
|
> etiquette_cli.py purge_empty_albums
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
search='''
|
||||||
|
search:
|
||||||
|
Search for photos and albums with complex operators.
|
||||||
|
|
||||||
|
> etiquette_cli.py search searchargs
|
||||||
|
> etiquette_cli.py search --album-search searchargs
|
||||||
|
|
||||||
|
Searchargs:
|
||||||
|
--area X-Y:
|
||||||
|
Photo/video width*height between X and Y.
|
||||||
|
|
||||||
|
--width X-Y:
|
||||||
|
Photo/video width between X and Y.
|
||||||
|
|
||||||
|
--height X-Y:
|
||||||
|
Photo/video height between X and Y.
|
||||||
|
|
||||||
|
--ratio X-Y:
|
||||||
|
Photo/video aspect ratio between X and Y.
|
||||||
|
|
||||||
|
--bytes X-Y:
|
||||||
|
File size in bytes between X and Y.
|
||||||
|
|
||||||
|
--duration X-Y:
|
||||||
|
Media duration between X and Y seconds.
|
||||||
|
|
||||||
|
--author X:
|
||||||
|
Photo authored by user with username X.
|
||||||
|
|
||||||
|
--created X-Y:
|
||||||
|
Photo creation date between X and Y unix timestamp.
|
||||||
|
|
||||||
|
--extension A,B,C:
|
||||||
|
Photo with any extension of A, B, C...
|
||||||
|
|
||||||
|
--extension_not A,B,C:
|
||||||
|
Photo without any extension of A, B, C...
|
||||||
|
|
||||||
|
--filename X:
|
||||||
|
Search terms for Photo's filename.
|
||||||
|
|
||||||
|
--has_tags yes/no/null:
|
||||||
|
If yes, Photo must have at least one tag.
|
||||||
|
If no, Photo must have no tags.
|
||||||
|
If null, doesn't matter.
|
||||||
|
|
||||||
|
--has_thumbnail yes/no/null:
|
||||||
|
|
||||||
|
--is_searchhidden yes/no/null:
|
||||||
|
|
||||||
|
--mimetype A,B,C:
|
||||||
|
Photo with any mimetype of A, B, C...
|
||||||
|
|
||||||
|
--tag_musts A,B,C:
|
||||||
|
Photo must have all tags A and B and C...
|
||||||
|
|
||||||
|
--tag_mays A,B,C:
|
||||||
|
Photo must have at least one tag of A, B, C...
|
||||||
|
|
||||||
|
--tag_forbids A,B,C:
|
||||||
|
Photo must not have any tags of A, B, C...
|
||||||
|
|
||||||
|
--tag_expression X:
|
||||||
|
Complex expression string to match tags.
|
||||||
|
|
||||||
|
--limit X:
|
||||||
|
Limit results to first X items.
|
||||||
|
|
||||||
|
--offset X:
|
||||||
|
Skip the first X items.
|
||||||
|
|
||||||
|
--orderby X-Y:
|
||||||
|
Order the results by property X in direction Y. E.g. created-desc or
|
||||||
|
bytes-asc.
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
show_associated_directories='''
|
||||||
|
show_associated_directories:
|
||||||
|
Show the associated directories for albums.
|
||||||
|
|
||||||
|
> etiquette_cli.py show_associated_directories
|
||||||
|
> etiquette_cli.py show_associated_directories --albums id id id
|
||||||
|
> etiquette_cli.py show_associated_directories --album-search searchargs
|
||||||
|
|
||||||
|
See search --help for more info about searchargs.
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
set_searchhidden='''
|
||||||
|
set_searchhidden:
|
||||||
|
Mark photos as searchhidden.
|
||||||
|
|
||||||
|
> etiquette_cli.py set_searchhidden --photos id id id
|
||||||
|
> etiquette_cli.py set_searchhidden --search searchargs
|
||||||
|
|
||||||
|
See search --help for more info about searchargs.
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
unset_searchhidden='''
|
||||||
|
unset_searchhidden:
|
||||||
|
Unmark photos as searchhidden.
|
||||||
|
|
||||||
|
> etiquette_cli.py unset_searchhidden --photos id id id
|
||||||
|
> etiquette_cli.py unset_searchhidden --search searchargs
|
||||||
|
|
||||||
|
See search --help for more info about searchargs.
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
tag_breplace='''
|
||||||
|
tag_breplace:
|
||||||
|
For all tags in the database, use find-and-replace to rename the tags.
|
||||||
|
|
||||||
|
> etiquette_cli.py tag_breplace replace_from replace_to
|
||||||
|
'''.strip(),
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
DOCSTRING = betterhelp.add_previews(DOCSTRING, SUB_DOCSTRINGS)
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
global LOG_LEVEL
|
global LOG_LEVEL
|
||||||
(LOG_LEVEL, argv) = vlogging.get_level_by_argv(argv)
|
(LOG_LEVEL, argv) = vlogging.get_level_by_argv(argv)
|
||||||
|
@ -432,11 +685,6 @@ def main(argv):
|
||||||
p_remove_tag.add_argument('--yes', dest='autoyes', action='store_true')
|
p_remove_tag.add_argument('--yes', dest='autoyes', action='store_true')
|
||||||
p_remove_tag.set_defaults(func=lambda args: add_remove_tag_argparse(args, action='remove'))
|
p_remove_tag.set_defaults(func=lambda args: add_remove_tag_argparse(args, action='remove'))
|
||||||
|
|
||||||
p_easybake = subparsers.add_parser('easybake')
|
|
||||||
p_easybake.add_argument('eb_strings', nargs='+')
|
|
||||||
p_easybake.add_argument('--yes', dest='autoyes', action='store_true')
|
|
||||||
p_easybake.set_defaults(func=easybake_argparse)
|
|
||||||
|
|
||||||
p_digest = subparsers.add_parser('digest', aliases=['digest_directory', 'digest-directory'])
|
p_digest = subparsers.add_parser('digest', aliases=['digest_directory', 'digest-directory'])
|
||||||
p_digest.add_argument('directory')
|
p_digest.add_argument('directory')
|
||||||
p_digest.add_argument('--no_albums', '--no-albums', dest='make_albums', action='store_false', default=True)
|
p_digest.add_argument('--no_albums', '--no-albums', dest='make_albums', action='store_false', default=True)
|
||||||
|
@ -445,6 +693,11 @@ def main(argv):
|
||||||
p_digest.add_argument('--yes', dest='autoyes', action='store_true')
|
p_digest.add_argument('--yes', dest='autoyes', action='store_true')
|
||||||
p_digest.set_defaults(func=digest_directory_argparse)
|
p_digest.set_defaults(func=digest_directory_argparse)
|
||||||
|
|
||||||
|
p_easybake = subparsers.add_parser('easybake')
|
||||||
|
p_easybake.add_argument('eb_strings', nargs='+')
|
||||||
|
p_easybake.add_argument('--yes', dest='autoyes', action='store_true')
|
||||||
|
p_easybake.set_defaults(func=easybake_argparse)
|
||||||
|
|
||||||
p_export_symlinks = subparsers.add_parser('export_symlinks', aliases=['export-symlinks'])
|
p_export_symlinks = subparsers.add_parser('export_symlinks', aliases=['export-symlinks'])
|
||||||
p_export_symlinks.add_argument('--destination', dest='destination', required=True)
|
p_export_symlinks.add_argument('--destination', dest='destination', required=True)
|
||||||
p_export_symlinks.add_argument('--dry', dest='dry_run', action='store_true')
|
p_export_symlinks.add_argument('--dry', dest='dry_run', action='store_true')
|
||||||
|
@ -509,20 +762,26 @@ def main(argv):
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
args = parser.parse_args(primary_args)
|
def pp(args):
|
||||||
|
args.photo_search_args = p_search.parse_args(photo_search_args) if photo_search_args else None
|
||||||
|
args.album_search_args = p_search.parse_args(album_search_args) if album_search_args else None
|
||||||
|
args.photo_id_args = [id for arg in photo_id_args for id in stringtools.comma_space_split(arg)]
|
||||||
|
args.album_id_args = [id for arg in album_id_args for id in stringtools.comma_space_split(arg)]
|
||||||
|
args.any_id_args = bool(
|
||||||
|
args.photo_search_args or
|
||||||
|
args.album_search_args or
|
||||||
|
args.photo_id_args or
|
||||||
|
args.album_id_args
|
||||||
|
)
|
||||||
|
return args
|
||||||
|
|
||||||
photo_search_args = p_search.parse_args(photo_search_args) if photo_search_args else None
|
return betterhelp.subparser_main(
|
||||||
album_search_args = p_search.parse_args(album_search_args) if album_search_args else None
|
primary_args,
|
||||||
photo_id_args = [id for arg in photo_id_args for id in stringtools.comma_space_split(arg)]
|
parser,
|
||||||
album_id_args = [id for arg in album_id_args for id in stringtools.comma_space_split(arg)]
|
main_docstring=DOCSTRING,
|
||||||
|
sub_docstrings=SUB_DOCSTRINGS,
|
||||||
args.photo_search_args = photo_search_args
|
args_postprocessor=pp,
|
||||||
args.album_search_args = album_search_args
|
)
|
||||||
args.photo_id_args = photo_id_args
|
|
||||||
args.album_id_args = album_id_args
|
|
||||||
args.any_id_args = bool(photo_search_args or album_search_args or photo_id_args or album_id_args)
|
|
||||||
|
|
||||||
return args.func(args)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
Loading…
Reference in a new issue