diff --git a/etiquette/photodb.py b/etiquette/photodb.py index 5809a7d..964ced8 100644 --- a/etiquette/photodb.py +++ b/etiquette/photodb.py @@ -550,6 +550,7 @@ class PDBPhotoMixin: tag_mays=None, tag_forbids=None, tag_expression=None, + within_directory=None, limit=None, offset=None, @@ -628,6 +629,11 @@ class PDBPhotoMixin: 'family AND (animals OR vacation)' 'family vacation outdoors' (implicitly AND each term) + within_directory: + A string or list of strings or pathclass Paths of directories. + Photos MUST have a `filepath` that is a child of one of these + directories. + QUERY OPTIONS limit: The maximum number of *successful* results to yield. @@ -675,6 +681,7 @@ class PDBPhotoMixin: has_thumbnail = searchhelpers.normalize_has_thumbnail(has_thumbnail) is_searchhidden = searchhelpers.normalize_is_searchhidden(is_searchhidden) mimetype = searchhelpers.normalize_extension(mimetype) + within_directory = searchhelpers.normalize_within_directory(within_directory, warning_bag=warning_bag) yield_albums = searchhelpers.normalize_yield_albums(yield_albums) if has_tags is False: @@ -761,6 +768,7 @@ class PDBPhotoMixin: 'tag_mays': tag_mays or None, 'tag_forbids': tag_forbids or None, 'tag_expression': giveback_tag_expression or None, + 'within_directory': within_directory or None, 'limit': limit, 'offset': offset or None, 'orderby': giveback_orderby or None, @@ -802,6 +810,17 @@ class PDBPhotoMixin: if mimetype: notnulls.add('extension') + if within_directory: + patterns = {f'{d.absolute_path}{os.sep}%' for d in within_directory} + clauses = ['filepath LIKE ?'] * len(patterns) + if len(clauses) > 1: + clauses = ' OR '.join(clauses) + clauses = f'({clauses})' + else: + clauses = clauses.pop() + wheres.append(clauses) + bindings.extend(patterns) + if has_tags is True: wheres.append('EXISTS (SELECT 1 FROM photo_tag_rel WHERE photoid == photos.id)') if has_tags is False: diff --git a/etiquette/searchhelpers.py b/etiquette/searchhelpers.py index 922ce08..a8aed61 100644 --- a/etiquette/searchhelpers.py +++ b/etiquette/searchhelpers.py @@ -9,6 +9,7 @@ from . import helpers from . import objects from voussoirkit import expressionmatch +from voussoirkit import pathclass from voussoirkit import sqlhelpers def expand_mmf(tag_musts, tag_mays, tag_forbids): @@ -385,6 +386,36 @@ def normalize_tag_expression(expression): return expression +def normalize_within_directory(paths, warning_bag=None): + if paths is None: + return set() + + if isinstance(paths, set): + pass + elif isinstance(paths, (str, pathclass.Path)): + paths = {paths} + elif isinstance(paths, (list, tuple)): + paths = set(paths) + else: + exc = TypeError(paths) + if warning_bag: + warning_bag.add(exc) + return set() + else: + raise exc + + paths = {pathclass.Path(p) for p in paths} + directories = {p for p in paths if p.is_dir} + not_directories = paths.difference(directories) + if not_directories: + exc = pathclass.NotDirectory(not_directories) + if warning_bag: + warning_bag.add(exc) + else: + raise exc + + return directories + def normalize_yield_albums(yield_albums): ''' See etiquette.helpers.truthystring. diff --git a/frontends/etiquette_flask/backend/endpoints/photo_endpoints.py b/frontends/etiquette_flask/backend/endpoints/photo_endpoints.py index 95247b4..eb39b19 100644 --- a/frontends/etiquette_flask/backend/endpoints/photo_endpoints.py +++ b/frontends/etiquette_flask/backend/endpoints/photo_endpoints.py @@ -387,6 +387,9 @@ def get_search_core(): search_generator = common.P.search(**search_kwargs) # Because of the giveback, first element is cleaned up kwargs search_kwargs = next(search_generator) + # Web UI users aren't allowed to use within_directory anyway, so don't + # show it to them. + search_kwargs.pop('within_directory', None) # print(search_kwargs) warnings = set()