mirror of
https://github.com/pkkid/python-plexapi
synced 2024-09-20 22:01:57 +00:00
Allow relative dates for search filters (#717)
* Add validation for relative date search filter values * Update search doc strings for searching using relative dates * Update library search tests for relative dates * Fix relative date search test * Automatically format negative sign in relative dates * Fix relative date search test
This commit is contained in:
parent
5584ef1d4f
commit
19fa6c1e50
2 changed files with 55 additions and 29 deletions
|
@ -852,10 +852,7 @@ class LibrarySection(PlexObject):
|
|||
if fieldType.type == 'boolean':
|
||||
value = int(bool(value))
|
||||
elif fieldType.type == 'date':
|
||||
if isinstance(value, datetime):
|
||||
value = int(value.timestamp())
|
||||
else:
|
||||
value = int(utils.toDatetime(value, '%Y-%m-%d').timestamp())
|
||||
value = self._validateFieldValueDate(value)
|
||||
elif fieldType.type == 'integer':
|
||||
value = int(value)
|
||||
elif fieldType.type == 'string':
|
||||
|
@ -866,12 +863,23 @@ class LibrarySection(PlexObject):
|
|||
value = next((f.key for f in filterChoices
|
||||
if matchValue in {f.key.lower(), f.title.lower()}), value)
|
||||
results.append(str(value))
|
||||
except ValueError:
|
||||
except (ValueError, AttributeError):
|
||||
raise BadRequest('Invalid value "%s" for filter field "%s", value should be type %s'
|
||||
% (value, filterField.key, fieldType.type)) from None
|
||||
|
||||
return results
|
||||
|
||||
def _validateFieldValueDate(self, value):
|
||||
""" Validates a filter date value. A filter date value can be a datetime object,
|
||||
a relative date (e.g. -30d), or a date in YYYY-MM-DD format.
|
||||
"""
|
||||
if isinstance(value, datetime):
|
||||
return int(value.timestamp())
|
||||
elif re.match(r'^-?\d+(mon|[smhdwy])$', value):
|
||||
return '-' + value.lstrip('-')
|
||||
else:
|
||||
return int(utils.toDatetime(value, '%Y-%m-%d').timestamp())
|
||||
|
||||
def _validateSortField(self, sort, libtype=None):
|
||||
""" Validates a filter sort field is available for the library.
|
||||
Returns the validated sort field string.
|
||||
|
@ -942,10 +950,7 @@ class LibrarySection(PlexObject):
|
|||
* See :func:`~plexapi.library.LibrarySection.listFilterChoices` to get a list of all available filter values.
|
||||
|
||||
The following filter fields are just some examples of the possible filters. The list is not exaustive,
|
||||
and not all filters apply to all library types. For tag type filters, a :class:`~plexapi.media.MediaTag`
|
||||
object, the exact name :attr:`MediaTag.tag` (*str*), or the exact id :attr:`MediaTag.id` (*int*) can be
|
||||
provided. For date type filters, either a ``datetime`` object or a date in ``YYYY-MM-DD`` (*str*) format
|
||||
can be provided. Multiple values can be ``OR`` together by providing a list of values.
|
||||
and not all filters apply to all library types.
|
||||
|
||||
* **actor** (:class:`~plexapi.media.MediaTag`): Search for the name of an actor.
|
||||
* **addedAt** (*datetime*): Search for items added before or after a date. See operators below.
|
||||
|
@ -973,6 +978,24 @@ class LibrarySection(PlexObject):
|
|||
* **writer** (:class:`~plexapi.media.MediaTag`): Search for the name of a writer.
|
||||
* **year** (*int*): Search for a specific year.
|
||||
|
||||
Tag type filter values can be a :class:`~plexapi.media.MediaTag` object, the exact name
|
||||
:attr:`MediaTag.tag` (*str*), or the exact id :attr:`MediaTag.id` (*int*).
|
||||
|
||||
Date type filter values can be a ``datetime`` object, a relative date using a one of the
|
||||
available date suffixes (e.g. ``30d``) (*str*), or a date in ``YYYY-MM-DD`` (*str*) format.
|
||||
|
||||
Relative date suffixes:
|
||||
|
||||
* ``s``: ``seconds``
|
||||
* ``m``: ``minutes``
|
||||
* ``h``: ``hours``
|
||||
* ``d``: ``days``
|
||||
* ``w``: ``weeks``
|
||||
* ``mon``: ``months``
|
||||
* ``y``: ``years``
|
||||
|
||||
Multiple values can be ``OR`` together by providing a list of values.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -1071,6 +1094,9 @@ class LibrarySection(PlexObject):
|
|||
# Title starts with Marvel and added before 2021-01-01
|
||||
library.search(**{"title<": "Marvel", "addedAt<<": "2021-01-01"})
|
||||
|
||||
# Added in the last 30 days using relative dates
|
||||
library.search(**{"addedAt>>": "30d"})
|
||||
|
||||
# Collection is James Bond and user rating is greater than 8
|
||||
library.search(**{"collection": "James Bond", "userRating>>": 8})
|
||||
|
||||
|
|
|
@ -470,27 +470,27 @@ def _test_library_search(library, obj):
|
|||
else:
|
||||
searchValue = value
|
||||
|
||||
searchFilter = {field.key + operator.key[:-1]: searchValue}
|
||||
results = library.search(libtype=obj.type, **searchFilter)
|
||||
|
||||
if operator.key.startswith("!") or operator.key.startswith(">>") and searchValue == 0:
|
||||
assert obj not in results
|
||||
else:
|
||||
assert obj in results
|
||||
_do_test_library_search(library, obj, field, operator, searchValue)
|
||||
|
||||
# Test search again using string tag and date
|
||||
if field.type in {"tag", "date"}:
|
||||
if field.type == "tag" and fieldAttr != 'contentRating':
|
||||
if field.type == "tag" and fieldAttr != "contentRating":
|
||||
if not isinstance(searchValue, list):
|
||||
searchValue = [searchValue]
|
||||
searchValue = [v.tag for v in searchValue]
|
||||
_do_test_library_search(library, obj, field, operator, searchValue)
|
||||
|
||||
elif field.type == "date":
|
||||
searchValue = searchValue.strftime("%Y-%m-%d")
|
||||
_do_test_library_search(library, obj, field, operator, searchValue)
|
||||
searchValue = "1s"
|
||||
_do_test_library_search(library, obj, field, operator, searchValue)
|
||||
|
||||
|
||||
def _do_test_library_search(library, obj, field, operator, searchValue):
|
||||
searchFilter = {field.key + operator.key[:-1]: searchValue}
|
||||
results = library.search(libtype=obj.type, **searchFilter)
|
||||
|
||||
if operator.key.startswith("!") or operator.key.startswith(">>") and searchValue == 0:
|
||||
if operator.key.startswith("!") or operator.key.startswith(">>") and (searchValue == 0 or searchValue == '1s'):
|
||||
assert obj not in results
|
||||
else:
|
||||
assert obj in results
|
||||
|
|
Loading…
Reference in a new issue