9 Commits

Author SHA1 Message Date
169cb3ac8c Merge 57915c6222 into 376f687c38 2025-03-02 01:22:02 +00:00
376f687c38 chore: questionable is not a recognized rating 2025-02-11 21:50:27 +01:00
4fd848abf2 doc: use docker compose instead of docker-compose
The minimum version requirements are rough guesses, in practice any decently modern docker installation should work.
2025-02-11 21:25:10 +01:00
57915c6222 Merge branch 'rr-:master' into more-tag-named-filters 2023-11-06 14:45:45 +10:00
a63545b64d Merge branch 'rr-:master' into more-tag-named-filters 2023-07-07 17:41:34 +10:00
3890579a1f Merge branch 'rr-:master' into more-tag-named-filters 2023-06-27 17:16:26 +10:00
61b7ed758f client: add help entries for named filters
Add help entries for named filters added in ca8e331
2023-06-18 21:53:05 +10:00
7b27465937 server/tests: add unit tests for named filters
Add unit tests for named filters added in ca8e331
2023-06-18 21:22:33 +10:00
ca8e3315bd server: add implies/suggests named filters
Add new named filters:
- suggests:     find tags that suggest the search criteria
- suggested-by: find tags that are suggested by the search criteria
- implies:      find tags that imply the search criteria
- implied-by:   find tags that are implied by the search criteria
2023-06-18 20:53:08 +10:00
7 changed files with 221 additions and 14 deletions

View File

@ -50,6 +50,22 @@
<td><code>post-count</code></td>
<td>alias of <code>usages</code></td>
</tr>
<tr>
<td><code>suggests</code></td>
<td>with given suggested tags (accepts wildcards)</td>
</tr>
<tr>
<td><code>implies</code></td>
<td>with given implied tags (accepts wildcards)</td>
</tr>
<tr>
<td><code>suggested-by</code></td>
<td>suggested by given tags (accepts wildcards)</td>
</tr>
<tr>
<td><code>implied-by</code></td>
<td>implied by given tags (accepts wildcards)</td>
</tr>
<tr>
<td><code>suggestion-count</code></td>
<td>with given number of suggestions</td>

View File

@ -789,7 +789,7 @@ data.
| `fav-time` | alias of `fav-date` |
| `feature-date` | featured at given date |
| `feature-time` | alias of `feature-time` |
| `safety` | having given safety. `<value>` can be either `safe`, `sketchy` (or `questionable`) or `unsafe`. |
| `safety` | having given safety. `<value>` can be either `safe`, `sketchy` or `unsafe`. |
| `rating` | alias of `safety` |
**Sort style tokens**

View File

@ -1,5 +1,5 @@
This assumes that you have Docker (version 17.05 or greater)
and Docker Compose (version 1.6.0 or greater) already installed.
This assumes that you have Docker (version 19.03 or greater)
and the Docker Compose CLI (version 1.27.0 or greater) already installed.
### Prepare things
@ -38,7 +38,7 @@ and Docker Compose (version 1.6.0 or greater) already installed.
This pulls the latest containers from docker.io:
```console
user@host:szuru$ docker-compose pull
user@host:szuru$ docker compose pull
```
If you have modified the application's source and would like to manually
@ -49,17 +49,17 @@ and Docker Compose (version 1.6.0 or greater) already installed.
For first run, it is recommended to start the database separately:
```console
user@host:szuru$ docker-compose up -d sql
user@host:szuru$ docker compose up -d sql
```
To start all containers:
```console
user@host:szuru$ docker-compose up -d
user@host:szuru$ docker compose up -d
```
To view/monitor the application logs:
```console
user@host:szuru$ docker-compose logs -f
user@host:szuru$ docker compose logs -f
# (CTRL+C to exit)
```
@ -84,13 +84,13 @@ and Docker Compose (version 1.6.0 or greater) already installed.
2. Build the containers:
```console
user@host:szuru$ docker-compose build
user@host:szuru$ docker compose build
```
That will attempt to build both containers, but you can specify `client`
or `server` to make it build only one.
If `docker-compose build` spits out:
If `docker compose build` spits out:
```
ERROR: Service 'server' failed to build: failed to parse platform : "" is an invalid component of "": platform specifier component must match "^[A-Za-z0-9_-]+$": invalid argument
@ -102,7 +102,7 @@ and Docker Compose (version 1.6.0 or greater) already installed.
user@host:szuru$ export DOCKER_BUILDKIT=1; export COMPOSE_DOCKER_CLI_BUILD=1
```
...and run `docker-compose build` again.
...and run `docker compose build` again.
*Note: If your changes are not taking effect in your builds, consider building
with `--no-cache`.*
@ -117,7 +117,7 @@ with `--no-cache`.*
run from docker:
```console
user@host:szuru$ docker-compose run server ./szuru-admin --help
user@host:szuru$ docker compose run server ./szuru-admin --help
```
will give you a breakdown on all available commands.

View File

@ -1,9 +1,7 @@
## Example Docker Compose configuration
##
## Use this as a template to set up docker-compose, or as guide to set up other
## Use this as a template to set up docker compose, or as guide to set up other
## orchestration services
version: '2'
services:
server:

View File

@ -96,6 +96,50 @@ class TagSearchConfig(BaseSearchConfig):
["implication-count"],
search_util.create_num_filter(model.Tag.implication_count),
),
(
["suggested-by"],
search_util.create_nested_filter(
model.Tag.tag_id,
model.TagSuggestion.child_id,
model.TagSuggestion.parent_id,
model.TagName.tag_id,
model.TagName.name,
search_util.create_str_filter,
),
),
(
["suggests"],
search_util.create_nested_filter(
model.Tag.tag_id,
model.TagSuggestion.parent_id,
model.TagSuggestion.child_id,
model.TagName.tag_id,
model.TagName.name,
search_util.create_str_filter,
),
),
(
["implied-by"],
search_util.create_nested_filter(
model.Tag.tag_id,
model.TagImplication.child_id,
model.TagImplication.parent_id,
model.TagName.tag_id,
model.TagName.name,
search_util.create_str_filter,
),
),
(
["implies"],
search_util.create_nested_filter(
model.Tag.tag_id,
model.TagImplication.parent_id,
model.TagImplication.child_id,
model.TagName.tag_id,
model.TagName.name,
search_util.create_str_filter,
),
),
]
)

View File

@ -226,3 +226,38 @@ def create_subquery_filter(
return query.filter(expression)
return wrapper
def create_nested_filter(
left_id_column: SaColumn,
right_id_column: SaColumn,
filter_column: SaColumn,
nested_id_column: SaColumn,
nested_filter_column: SaColumn,
filter_factory: SaColumn,
subquery_decorator: Callable[[SaQuery], None] = None,
) -> Filter:
filter_func = filter_factory(nested_filter_column)
def wrapper(
query: SaQuery,
criterion: Optional[criteria.BaseCriterion],
negated: bool,
) -> SaQuery:
assert criterion
nested = db.session.query(nested_id_column.label("foreign_id"))
nested = nested.options(sa.orm.lazyload("*"))
nested = filter_func(nested, criterion, False)
nested = nested.subquery("t")
subquery = db.session.query(right_id_column.label("foreign_id"))
if subquery_decorator:
subquery = subquery_decorator(subquery)
subquery = subquery.options(sa.orm.lazyload("*"))
subquery = subquery.filter(filter_column.in_(nested))
subquery = subquery.subquery("t")
expression = left_id_column.in_(subquery)
if negated:
expression = ~expression
return query.filter(expression)
return wrapper

View File

@ -370,6 +370,120 @@ def test_filter_by_implication_count(
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize(
"input,expected_tag_names",
[
("suggests:sug1", ["t1", "t3"]),
("suggests:sug2", ["t1"]),
("suggests:sug3", ["t2"]),
("suggests:t1", []),
("-suggests:sug1", ["sug1", "sug2", "sug3", "t2"]),
],
)
def test_filter_by_suggests_tags(
verify_unpaged, tag_factory, input, expected_tag_names
):
sug1 = tag_factory(names=["sug1"])
sug2 = tag_factory(names=["sug2"])
sug3 = tag_factory(names=["sug3"])
tag1 = tag_factory(names=["t1"])
tag2 = tag_factory(names=["t2"])
tag3 = tag_factory(names=["t3"])
db.session.add_all([sug1, sug3, tag2, sug2, tag1, tag3])
tag1.suggestions.append(sug1)
tag1.suggestions.append(sug2)
tag2.suggestions.append(sug3)
tag3.suggestions.append(sug1)
db.session.flush()
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize(
"input,expected_tag_names",
[
("suggested-by:t1", ["sug1", "sug2"]),
("suggested-by:t2", ["sug3"]),
("suggested-by:t3", ["sug4", "t2"]),
("-suggested-by:t3", ["sug1", "sug2", "sug3", "t1", "t3",]),
],
)
def test_filter_by_suggests_by_tags(
verify_unpaged, tag_factory, input, expected_tag_names
):
sug1 = tag_factory(names=["sug1"])
sug2 = tag_factory(names=["sug2"])
sug3 = tag_factory(names=["sug3"])
sug4 = tag_factory(names=["sug4"])
tag1 = tag_factory(names=["t1"])
tag2 = tag_factory(names=["t2"])
tag3 = tag_factory(names=["t3"])
db.session.add_all([sug1, sug3, tag2, sug2, tag1, tag3, sug4])
tag1.suggestions.append(sug1)
tag1.suggestions.append(sug2)
tag2.suggestions.append(sug3)
tag3.suggestions.append(tag2)
tag3.suggestions.append(sug4)
db.session.flush()
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize(
"input,expected_tag_names",
[
("implies:sug1", ["t1", "t3"]),
("implies:sug2", ["t1"]),
("implies:sug3", ["t2"]),
("implies:t1", []),
("-implies:sug1", ["sug1", "sug2", "sug3", "t2"]),
],
)
def test_filter_by_implies_tags(
verify_unpaged, tag_factory, input, expected_tag_names
):
sug1 = tag_factory(names=["sug1"])
sug2 = tag_factory(names=["sug2"])
sug3 = tag_factory(names=["sug3"])
tag1 = tag_factory(names=["t1"])
tag2 = tag_factory(names=["t2"])
tag3 = tag_factory(names=["t3"])
db.session.add_all([sug1, sug3, tag2, sug2, tag1, tag3])
tag1.implications.append(sug1)
tag1.implications.append(sug2)
tag2.implications.append(sug3)
tag3.implications.append(sug1)
db.session.flush()
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize(
"input,expected_tag_names",
[
("implied-by:t1", ["sug1", "sug2"]),
("implied-by:t2", ["sug3"]),
("implied-by:t3", ["sug4", "t2",]),
("-implied-by:t3", ["sug1", "sug2", "sug3", "t1", "t3",]),
],
)
def test_filter_by_implied_by_tags(
verify_unpaged, tag_factory, input, expected_tag_names
):
sug1 = tag_factory(names=["sug1"])
sug2 = tag_factory(names=["sug2"])
sug3 = tag_factory(names=["sug3"])
sug4 = tag_factory(names=["sug4"])
tag1 = tag_factory(names=["t1"])
tag2 = tag_factory(names=["t2"])
tag3 = tag_factory(names=["t3"])
db.session.add_all([sug1, sug3, tag2, sug2, tag1, tag3, sug4])
tag1.implications.append(sug1)
tag1.implications.append(sug2)
tag2.implications.append(sug3)
tag3.implications.append(tag2)
tag3.implications.append(sug4)
db.session.flush()
verify_unpaged(input, expected_tag_names)
@pytest.mark.parametrize(
"input,expected_tag_names",
[