Skip to content

Commit

Permalink
Merge pull request #752
Browse files Browse the repository at this point in the history
v5.0.2
  • Loading branch information
MatteoCampinoti94 authored Jan 8, 2025
2 parents 270a4c6 + 3536f1d commit 27e6071
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 27 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## v5.0.2

### Changes

* When using the `@file` operator in queries, the values are matched exactly with the [SQL
`IN` operator](https://sqlite.org/src/doc/tip/src/in-operator.md).
* The `@like` operator no longer has any effect when use in conjunction with `@file`

## v5.0.1

### Changes
Expand Down
2 changes: 1 addition & 1 deletion digiarch/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "5.0.1"
__version__ = "5.0.2"
2 changes: 1 addition & 1 deletion digiarch/commands/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def cmd_log(ctx: Context, runs_only: bool, order: str, limit: int):
query: TQuery = []

if runs_only:
query = [("operation", "%:start", True), ("operation", "%:end", True)]
query = [("operation", "%:start", "like"), ("operation", "%:end", "like")]

with open_database(ctx, get_avid(ctx)) as database:
events: list[Event] = list(query_table(database.log, query, [("time", order)], limit))
Expand Down
59 changes: 35 additions & 24 deletions digiarch/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

M = TypeVar("M", bound=BaseModel)
FC = TypeVar("FC", bound=Callable[..., Any])
TQuery = list[tuple[str, str | bool | Type[Ellipsis] | None, bool]]
TQuery = list[tuple[str, str | bool | Type[Ellipsis] | list[str] | None, str]] # field name, value(s), operation

token_quotes = re_compile(r'(?<!\\)"((?:[^"]|(?<=\\)")*)"')
# noinspection RegExpUnnecessaryNonCapturingGroup
token_expr = re_compile(r"(?:\0([^\0]+)\0|(?<!\\)\s+)")
token_expr = re_compile(r"(?:\x00([^\x00]+)\x00|(?<!\\)\s+)")


def query_to_where(query: TQuery) -> tuple[str, list[str]]:
Expand All @@ -34,21 +34,32 @@ def query_to_where(query: TQuery) -> tuple[str, list[str]]:
for field, values in query_fields.items():
where_field: list[str] = []

for value, like in values:
if value is None:
where_field.append(f"{field} is null")
elif value is Ellipsis:
where_field.append(f"{field} is not null")
elif value is True:
where_field.append(f"{field} is true")
elif value is False:
where_field.append(f"{field} is false")
elif like:
where_field.append(f"{field} like ?")
parameters.append(value)
else:
where_field.append(f"{field} = ?")
parameters.append(value)
for value, op in values:
match (value, op):
case None, "is":
where_field.append(f"{field} is null")
case None, "is not":
where_field.append(f"{field} is not null")
case True, "is":
where_field.append(f"{field} is true")
case True, "is not":
where_field.append(f"{field} is false")
case False, "is":
where_field.append(f"{field} is false")
case False, "is not":
where_field.append(f"{field} is true")
case _, "in" if isinstance(value, list):
where_field.append(f"{field} in ({','.join(['?'] * len(value))})")
parameters.extend(value)
case _, "in" if isinstance(value, str):
where_field.append(f"instr({field}, ?) != 0")
parameters.append(value)
case _, "=":
where_field.append(f"{field} = ?")
parameters.append(value)
case _, "like":
where_field.append(f"{field} like ?")
parameters.append(value)

where.append(f"({' or '.join(where_field)})")

Expand All @@ -66,13 +77,13 @@ def tokenize_query(query_string: str, default_field: str, allowed_fields: list[s

for token in tokens:
if token == "@null":
query_tokens.append((field, None, False))
query_tokens.append((field, None, "is"))
elif token == "@notnull":
query_tokens.append((field, ..., True))
query_tokens.append((field, None, "is not"))
elif token == "@true":
query_tokens.append((field, True, False))
query_tokens.append((field, True, "is"))
elif token == "@false":
query_tokens.append((field, False, False))
query_tokens.append((field, True, "is not"))
elif token == "@like":
like = True
elif token == "@file":
Expand All @@ -84,15 +95,15 @@ def tokenize_query(query_string: str, default_field: str, allowed_fields: list[s
from_file = False
elif from_file:
with open(token) as fh:
query_tokens.extend([(field, line_, like) for line in fh.readlines() if (line_ := line.strip())])
query_tokens.append((field, [line for l in fh.readlines() if (line := l.rstrip("\r\n"))], "in"))
else:
query_tokens.append((field, token, like))
query_tokens.append((field, token, "like" if like else "="))

return query_tokens


def argument_query(required: bool, default: str, allowed_fields: list[str] | None = None) -> Callable[[FC], FC]:
def callback(ctx: Context, param: Parameter, value: str | None) -> list[tuple[str, str, bool]]:
def callback(ctx: Context, param: Parameter, value: str | None) -> TQuery:
if not (value := value or "").strip() and required:
raise MissingParameter(None, ctx, param)
if not value:
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "digiarch"
version = "5.0.1"
version = "5.0.2"
description = "Tools for the Digital Archive Project at Aarhus Stadsarkiv"
authors = ["Aarhus Stadsarkiv <[email protected]>"]
license = "GPL-3.0"
Expand Down Expand Up @@ -101,6 +101,7 @@ ignore = [
"DTZ006", # datetime.datetime.fromtimestamp() called without a tz argument
"E501", # line to long, to many false positives, gets handled by black
"E712", # comparison to True/False, we ignore because we use sqlalchemy
"E741", # Ambiguous variable name: `l`
"FBT001", # boolean arguement in function definition
"INP001", # implicit namespace without __init__ (throws errors in tests)
"ISC001", # check for implicit concatanation of str on one line, not compatabil with black.
Expand Down

0 comments on commit 27e6071

Please sign in to comment.