Added --regex query option, closes #433

This commit is contained in:
Rhet Turnbull
2021-04-20 20:17:44 -07:00
parent 7c4b28a35c
commit 44966c6736
20 changed files with 196 additions and 14 deletions

View File

@@ -331,6 +331,13 @@ Options:
valid and equivalent sizes: '1048576'
'1.048576MB', '1 MiB'.
--regex REGEX TEMPLATE Search for photos where TEMPLATE matches
regular expression REGEX. For example, to find
photos in an album that begins with 'Beach': '
--regex "^Beach" "{album}"'. You may specify
more than one regular expression match by
repeating '--regex' with different arguments.
--query-eval CRITERIA Evaluate CRITERIA to filter photos. CRITERIA
will be evaluated in context of the following
python list comprehension: `photos = [photo

View File

@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 9272cec551db6f07fa8ab4768c3e639d
config: 2df8df9cf893034d0610c8d03a310fef
tags: 645f666f9bcd5a90fca523b33c5a78b7

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Overview: module code &#8212; osxphotos 0.42.7 documentation</title>
<title>Overview: module code &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.photoinfo.photoinfo &#8212; osxphotos 0.41.6 documentation</title>
<title>osxphotos.photoinfo.photoinfo &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos.photosdb.photosdb &#8212; osxphotos 0.42.6 documentation</title>
<title>osxphotos.photosdb.photosdb &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="../../../_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../../../_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="../../../" src="../../../_static/documentation_options.js"></script>
@@ -3201,6 +3201,19 @@
<span class="k">if</span> <span class="n">bitmath</span><span class="o">.</span><span class="n">Byte</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">original_filesize</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">options</span><span class="o">.</span><span class="n">max_size</span>
<span class="p">]</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">regex</span><span class="p">:</span>
<span class="n">flags</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">IGNORECASE</span> <span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">ignore_case</span> <span class="k">else</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">regex</span><span class="p">,</span> <span class="n">template</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">regex</span><span class="p">:</span>
<span class="n">regex</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="n">regex</span><span class="p">,</span> <span class="n">flags</span><span class="p">)</span>
<span class="n">photo_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">photos</span><span class="p">:</span>
<span class="n">rendered</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">render_template</span><span class="p">(</span><span class="n">template</span><span class="p">,</span> <span class="n">none_str</span><span class="o">=</span><span class="s2">&quot;&quot;</span><span class="p">)</span>
<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">rendered</span><span class="p">:</span>
<span class="k">if</span> <span class="n">regex</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">value</span><span class="p">):</span>
<span class="n">photo_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="k">break</span>
<span class="n">photos</span> <span class="o">=</span> <span class="n">photo_list</span>
<span class="k">if</span> <span class="n">options</span><span class="o">.</span><span class="n">query_eval</span><span class="p">:</span>
<span class="k">for</span> <span class="n">q</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">query_eval</span><span class="p">:</span>
<span class="n">query_string</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;[photo for photo in photos if </span><span class="si">{</span><span class="n">q</span><span class="si">}</span><span class="s2">]&quot;</span>

View File

@@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.42.7',
VERSION: '0.42.9',
LANGUAGE: 'None',
COLLAPSE_INDEX: false,
BUILDER: 'html',

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.42.7 documentation</title>
<title>osxphotos command line interface (CLI) &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
@@ -507,6 +507,12 @@ to modify this behavior.</p>
<dd><p>Search for photos with size &lt;= SIZE bytes. The size evaluated is the photos original size (when imported to Photos). Size may be specified as integer bytes or using SI or NIST units. For example, the following are all valid and equivalent sizes: 1048576 1.048576MB, 1 MiB.</p>
</dd></dl>
<dl class="std option">
<dt id="cmdoption-osxphotos-export-regex">
<code class="sig-name descname"><span class="pre">--regex</span></code><code class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></code><a class="headerlink" href="#cmdoption-osxphotos-export-regex" title="Permalink to this definition"></a></dt>
<dd><p>Search for photos where TEMPLATE matches regular expression REGEX. For example, to find photos in an album that begins with Beach: regex “^Beach” “{album}”’. You may specify more than one regular expression match by repeating regex with different arguments.</p>
</dd></dl>
<dl class="std option">
<dt id="cmdoption-osxphotos-export-query-eval">
<code class="sig-name descname"><span class="pre">--query-eval</span></code><code class="sig-prename descclassname"> <span class="pre">&lt;CRITERIA&gt;</span></code><a class="headerlink" href="#cmdoption-osxphotos-export-query-eval" title="Permalink to this definition"></a></dt>
@@ -1371,6 +1377,12 @@ if more than one option is provided, they are treated as “AND”
<dd><p>Search for photos with size &lt;= SIZE bytes. The size evaluated is the photos original size (when imported to Photos). Size may be specified as integer bytes or using SI or NIST units. For example, the following are all valid and equivalent sizes: 1048576 1.048576MB, 1 MiB.</p>
</dd></dl>
<dl class="std option">
<dt id="cmdoption-osxphotos-query-regex">
<code class="sig-name descname"><span class="pre">--regex</span></code><code class="sig-prename descclassname"> <span class="pre">&lt;REGEX</span> <span class="pre">TEMPLATE&gt;</span></code><a class="headerlink" href="#cmdoption-osxphotos-query-regex" title="Permalink to this definition"></a></dt>
<dd><p>Search for photos where TEMPLATE matches regular expression REGEX. For example, to find photos in an album that begins with Beach: regex “^Beach” “{album}”’. You may specify more than one regular expression match by repeating regex with different arguments.</p>
</dd></dl>
<dl class="std option">
<dt id="cmdoption-osxphotos-query-query-eval">
<code class="sig-name descname"><span class="pre">--query-eval</span></code><code class="sig-prename descclassname"> <span class="pre">&lt;CRITERIA&gt;</span></code><a class="headerlink" href="#cmdoption-osxphotos-query-query-eval" title="Permalink to this definition"></a></dt>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; osxphotos 0.42.7 documentation</title>
<title>Index &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
@@ -822,6 +822,15 @@
<li><a href="cli.html#cmdoption-osxphotos-export-query-eval">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-query-eval">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
--regex &lt;REGEX TEMPLATE&gt;
<ul>
<li><a href="cli.html#cmdoption-osxphotos-export-regex">osxphotos-export command line option</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-regex">osxphotos-query command line option</a>
</li>
</ul></li>
<li>
@@ -1640,6 +1649,8 @@
<li><a href="cli.html#cmdoption-osxphotos-export-portrait">--portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-query-eval">--query-eval &lt;CRITERIA&gt;</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-regex">--regex &lt;REGEX TEMPLATE&gt;</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-export-replace-keywords">--replace-keywords</a>
</li>
@@ -1898,6 +1909,8 @@
<li><a href="cli.html#cmdoption-osxphotos-query-portrait">--portrait</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-query-eval">--query-eval &lt;CRITERIA&gt;</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-regex">--regex &lt;REGEX TEMPLATE&gt;</a>
</li>
<li><a href="cli.html#cmdoption-osxphotos-query-screenshot">--screenshot</a>
</li>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.42.7 documentation</title>
<title>Welcome to osxphotoss documentation! &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos &#8212; osxphotos 0.42.7 documentation</title>
<title>osxphotos &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>

Binary file not shown.

Binary file not shown.

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>osxphotos package &#8212; osxphotos 0.42.7 documentation</title>
<title>osxphotos package &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>

View File

@@ -5,7 +5,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; osxphotos 0.42.7 documentation</title>
<title>Search &#8212; osxphotos 0.42.9 documentation</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
""" version info """
__version__ = "0.42.8"
__version__ = "0.42.9"

View File

@@ -474,6 +474,15 @@ def QUERY_OPTIONS(f):
"Size may be specified as integer bytes or using SI or NIST units. "
"For example, the following are all valid and equivalent sizes: '1048576' '1.048576MB', '1 MiB'.",
),
o(
"--regex",
metavar="REGEX TEMPLATE",
nargs=2,
multiple=True,
help="Search for photos where TEMPLATE matches regular expression REGEX. "
"For example, to find photos in an album that begins with 'Beach': '--regex \"^Beach\" \"{album}\"'. "
"You may specify more than one regular expression match by repeating '--regex' with different arguments.",
),
o(
"--query-eval",
metavar="CRITERIA",
@@ -1027,6 +1036,7 @@ def export(
not_in_album,
min_size,
max_size,
regex,
query_eval,
):
"""Export photos from the Photos database.
@@ -1177,6 +1187,7 @@ def export(
not_in_album = cfg.not_in_album
min_size = cfg.min_size
max_size = cfg.max_size
regex = cfg.regex
query_eval = cfg.query_eval
# config file might have changed verbose
@@ -1481,6 +1492,7 @@ def export(
name=name,
min_size=min_size,
max_size=max_size,
regex=regex,
query_eval=query_eval,
)
@@ -1780,6 +1792,7 @@ def query(
not_in_album,
min_size,
max_size,
regex,
query_eval,
):
"""Query the Photos database using 1 or more search options;
@@ -1810,6 +1823,7 @@ def query(
query_eval,
min_size,
max_size,
regex,
]
exclusive = [
(favorite, not_favorite),
@@ -1934,6 +1948,7 @@ def query(
min_size=min_size,
max_size=max_size,
query_eval=query_eval,
regex=regex,
)
try:

View File

@@ -3168,6 +3168,19 @@ class PhotosDB:
if bitmath.Byte(p.original_filesize) <= options.max_size
]
if options.regex:
flags = re.IGNORECASE if options.ignore_case else 0
for regex, template in options.regex:
regex = re.compile(regex, flags)
photo_list = []
for p in photos:
rendered, _ = p.render_template(template, none_str="")
for value in rendered:
if regex.search(value):
photo_list.append(p)
break
photos = photo_list
if options.query_eval:
for q in options.query_eval:
query_string = f"[photo for photo in photos if {q}]"

View File

@@ -1,7 +1,7 @@
""" QueryOptions class for PhotosDB.query """
from dataclasses import dataclass
from typing import Optional, Iterable
from typing import Optional, Iterable, Tuple
import datetime
import bitmath
@@ -76,6 +76,7 @@ class QueryOptions:
name: Optional[Iterable[str]] = None
min_size: Optional[bitmath.Byte] = None
max_size: Optional[bitmath.Byte] = None
regex: Optional[Iterable[Tuple[str, str]]] = None
query_eval: Optional[Iterable[str]] = None
def asdict(self):

View File

@@ -5993,3 +5993,111 @@ def test_query_min_size_error():
)
assert result.exit_code != 0
def test_query_regex_1():
""" test query --regex against title """
import json
import os
import os.path
import osxphotos
from osxphotos.cli import query
runner = CliRunner()
cwd = os.getcwd()
result = runner.invoke(
query,
[
"--json",
"--db",
os.path.join(cwd, PHOTOS_DB_15_7),
"--regex",
"I found",
"{title}",
],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
assert len(json_got) == 1
def test_query_regex_2():
""" test query --regex with no match"""
import json
import os
import os.path
import osxphotos
from osxphotos.cli import query
runner = CliRunner()
cwd = os.getcwd()
result = runner.invoke(
query,
[
"--json",
"--db",
os.path.join(cwd, PHOTOS_DB_15_7),
"--regex",
"{title}",
"i Found",
],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
assert len(json_got) == 0
def test_query_regex_3():
""" test query --regex with --ignore-case """
import json
import os
import os.path
import osxphotos
from osxphotos.cli import query
runner = CliRunner()
cwd = os.getcwd()
result = runner.invoke(
query,
[
"--json",
"--db",
os.path.join(cwd, PHOTOS_DB_15_7),
"--regex",
"i Found",
"{title}",
"--ignore-case",
],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
assert len(json_got) == 1
def test_query_regex_4():
""" test query --regex against album """
import json
import os
import os.path
import osxphotos
from osxphotos.cli import query
runner = CliRunner()
cwd = os.getcwd()
result = runner.invoke(
query,
[
"--json",
"--db",
os.path.join(cwd, PHOTOS_DB_15_7),
"--regex",
"^Test",
"{album}",
],
)
assert result.exit_code == 0
json_got = json.loads(result.output)
assert len(json_got) == 2