8 Commits

Author SHA1 Message Date
relikd
5bbe6d2360 chore: bump v1.0.0 2023-03-04 18:19:26 +01:00
relikd
1188216788 fix: remove duplicates from vobj.children 2022-12-22 00:18:25 +01:00
relikd
b603fb9dd2 feat: add unique=False to vgroups filter 2022-12-21 20:12:13 +01:00
relikd
05b9fbf20a refactor: vgroups set default attr recursive=True 2022-12-21 19:50:27 +01:00
relikd
7039fb3a63 fix: yield unique vgroups while keeping sort order 2022-12-21 19:22:59 +01:00
relikd
4689e9fccb docs: update changelog + bump version 2022-12-21 01:03:53 +01:00
relikd
2e7cc026f6 fix: keep order of vgroups filter if no order_by provided 2022-12-21 00:50:32 +01:00
relikd
14a7fe848f docs: rearrange example into sections 2022-12-21 00:48:52 +01:00
6 changed files with 79 additions and 32 deletions

View File

@@ -8,6 +8,27 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
## [Unreleased]
## [1.0.0] 2023-03-04
### Added
- `vgroups` filter now supports `unique=False` to return a list of all entries including duplicates (default: `True`)
### Fixed
- Preserves original sort order in `vgroups` filter if `unique=True`
- Remove duplicates from `GroupBySource` children
### Changed
- `vgroups` filter uses `recursive=True` by default
## [0.9.9] 2022-12-21
### Fixed
- Keep original sorting order in `vgroups` filter if no `order_by` is set
## [0.9.8] 2022-12-20
### Added
@@ -142,7 +163,9 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
Initial release
[Unreleased]: https://github.com/relikd/lektor-groupby-plugin/compare/v0.9.8...HEAD
[Unreleased]: https://github.com/relikd/lektor-groupby-plugin/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/relikd/lektor-groupby-plugin/compare/v0.9.9...v1.0.0
[0.9.9]: https://github.com/relikd/lektor-groupby-plugin/compare/v0.9.8...v0.9.9
[0.9.8]: https://github.com/relikd/lektor-groupby-plugin/compare/v0.9.7...v0.9.8
[0.9.7]: https://github.com/relikd/lektor-groupby-plugin/compare/v0.9.6...v0.9.7
[0.9.6]: https://github.com/relikd/lektor-groupby-plugin/compare/v0.9.5...v0.9.6

View File

@@ -3,7 +3,6 @@
A generic grouping / clustering plugin.
Can be used for tagging or similar tasks.
The grouping algorithm is performed once.
Contrary to, at least, cubic runtime if doing the same with Pad queries.
Install this plugin or modify your Lektor project file:

View File

@@ -1,15 +1,16 @@
# Usage
Overview:
- [quick config example](#quick-config) shows how you can use the plugin config to setup a quick and easy tagging system.
- [config example](#config-file) shows how you can use the plugin config to setup a quick and easy tagging system.
- [simple example](#simple-example) goes into detail how to use it in your own plugin.
- [advanced example](#advanced-example) touches on the potentials of the plugin.
- [advanced example](#advanced-example) touches on the potentials of plugin development.
- [Misc](#misc) shows other use-cases.
After reading this tutorial, have a look at other plugins that use `lektor-groupby`:
- [lektor-inlinetags](https://github.com/relikd/lektor-inlinetags-plugin)
## About
To use the groupby plugin you have to add an attribute to your model file.
@@ -35,15 +36,9 @@ The attribute name is later used for grouping.
## Quick config
## Config File
Relevant files:
- [`configs/groupby.ini`](./configs/groupby.ini)
- [`templates/example-config.html`](./templates/example-config.html)
The easiest way to add tags to your site is by defining the `groupby.ini` config file.
The easiest way to add tags to your site is by defining the [`configs/groupby.ini`](./configs/groupby.ini) file.
```ini
[testA]
@@ -67,9 +62,12 @@ url_suffix = .page.
title = "Tagged: " ~ this.key_obj
[testA.key_map]
Blog = News
C# = c-sharp
```
### Config: Main Section
The configuration parameter are:
1. The section title (`testA`) must be one of the attribute variables we defined in our model.
@@ -91,7 +89,7 @@ The configuration parameter are:
These single-line fields are then expanded to lists as well.
If you do not provide the `split` option, the whole field value will be used as tagname.
6. The `enabled` parameter allows you to quickly disable the grouping.
7. The `key_obj_fn` parameter (jinja2) accepts any function-like snippet or function call.
7. The `key_obj_fn` parameter (jinja) accepts any function-like snippet or function call.
The context provides two variables, `X` and `ARGS`.
The former is the raw value of the grouping, this may be a text field, markdown, or whatever custom type you have provided.
The latter is a named tuple with `record`, `key`, and `field` values (see [simple example](#simple-example)).
@@ -101,13 +99,22 @@ The configuration parameter are:
You can have multiple listeners, e.g., one for `/blog/` and another for `/projects/`.
Just create as many custom attributes as you like, each having its own section (and subsections).
### Config Subsection: .children
The `.children` subsection currently has a single config field: `order_by`.
The usual [order-by](https://www.getlektor.com/docs/guides/page-order/) rules apply (comma separated list of keys with `-` for reversed order).
The order-by key can be anything of the page attributes of the children.
The `.pagination` subsection accepts the same configuration options as the Lektor pagination [model](https://www.getlektor.com/docs/models/children/#pagination) and [guide](https://www.getlektor.com/docs/guides/pagination/).
### Config Subsection: .pagination
The `.pagination` subsection accepts the same configuration options as the default Lektor pagination ([model](https://www.getlektor.com/docs/models/children/#pagination), [guide](https://www.getlektor.com/docs/guides/pagination/)).
Plus, an additional `url_suffix` parameter if you would like to customize the URL scheme.
### Config Subsection: .fields
The `.fields` subsection is a list of key-value pairs which will be added as attributes to your grouping.
You can access them in your template (e.g., `{{this.title}}`).
All of the `.fields` values are evaluted in a jinja context, so be cautious when using plain strings.
@@ -122,11 +129,6 @@ The built-in field attributes are:
- `children`: the elements of the grouping (a `Query` of `Record` type)
- `config`: configuration object (see below)
Without any changes, the `key` value will just be `slugify(key_obj)`.
However, the `.key_map` subsection will replace `key_obj` with whatever replacement value is provided in the `.key_map` and then slugify.
You could, for example, add a `C# = c-sharp` mapping, which would otherwise just be slugified to `c`.
This is equivalent to `slugify(key_map.get(key_obj))`.
The `config` attribute contains the values that created the group:
- `key`: attribute key, e.g., `TestA`
@@ -142,9 +144,21 @@ The `config` attribute contains the values that created the group:
- `pagination`: raw values from `TestA.pagination`
- `order_by`: list of key-strings from `TestA.children.order_by`
In your template file you have access to the config, attributes, fields, and children (Pages):
```jinja2
### Config Subsection: .key_map
Without any changes, the `key` value will just be `slugify(key_obj)`.
However, the `.key_map` subsection will replace `key_obj` with whatever replacement value is provided in the mapping and is then slugified.
Take the given example, `C# = c-sharp`, which would otherwise be slugified to `c`.
This is equivalent to `slugify(key_map.get(key_obj))`.
### Config Template
In your template file ([`templates/example-config.html`](./templates/example-config.html)), you have access to the aforementioned attributes:
```jinja
<h2>{{ this.title }}</h2>
<p>Key: {{ this.key }}, Attribute: {{ this.config.key }}</p>
<ul>
@@ -215,7 +229,7 @@ field = args.record[k.fieldKey].blocks[k.flowIndex].get(k.flowKey)
Again, you can use all properties in your template.
```jinja2
```jinja
<p>Custom field date: {{this.date}}</p>
<ul>
{%- for child in this.children %}
@@ -300,8 +314,8 @@ template = example-advanced.html
match = {{([^}]{1,32})}}
```
The config file takes the same parameters as the [config example](#quick-config).
We introduced a new config option `testC.pattern.match`.
The config file takes the same parameters as the [config example](#config-file).
We introduce a new config option `testC.pattern.match`.
This regular expression matches `{{` + any string less than 32 characters + `}}`.
Notice, the parenthesis (`()`) will match only the inner part, thus the replace function (`re.sub`) removes the `{{}}`.
@@ -318,7 +332,7 @@ You can combine this with the next use-case.
### Index pages & Group query + filter
```jinja2
```jinja
{%- for x in this|vgroups(keys=['TestA', 'TestB'], fields=[], flows=[], recursive=True, order_by='key_obj') %}
<a href="{{ x|url }}">({{ x.key_obj }})</a>
{%- endfor %}

View File

@@ -1,5 +1,5 @@
from lektor.context import get_ctx
from typing import TYPE_CHECKING, Set, Union, Iterable, Iterator
from typing import TYPE_CHECKING, Set, List, Dict, Union, Iterable, Iterator
import weakref
from .util import split_strip
if TYPE_CHECKING:
@@ -49,8 +49,9 @@ class VGroups:
*,
fields: Union[str, Iterable[str], None] = None,
flows: Union[str, Iterable[str], None] = None,
recursive: bool = False,
order_by: Union[str, Iterable[str], None] = None,
recursive: bool = True,
unique: bool = True,
) -> Iterator['GroupBySource']:
''' Extract all referencing groupby virtual objects from a page. '''
# prepare filter
@@ -68,7 +69,12 @@ class VGroups:
GroupByRef.of(builder).make_once(keys) # ensure did cluster before use
# find groups
proc_list = [record]
done_list = set() # type: Set[GroupBySource]
# Note: An ordered Set would be more approptiate but there is none.
# So lets use the insert order of dict (guaranteed since Python 3.7)
_only_uniques = {} # type: Dict[GroupBySource, None]
_w_duplicates = [] # type: List[GroupBySource]
while proc_list:
page = proc_list.pop(0)
if recursive and hasattr(page, 'children'):
@@ -80,7 +86,12 @@ class VGroups:
continue
if keys and vobj().config.key not in keys:
continue
done_list.add(vobj())
if unique:
_only_uniques[vobj()] = None # we only need the keys()
else:
_w_duplicates.append(vobj())
done_list = _only_uniques if unique else _w_duplicates
# manage config dependencies
deps = set() # type: Set[str]

View File

@@ -46,7 +46,7 @@ class GroupBySource(VirtualSourceObject):
self.page_num = page_num
def append_child(self, child: 'Record', key_obj: Any) -> None:
if child not in self.__children:
if child.path not in self.__children:
self.__children.append(child.path)
# __key_obj_map is later used to find most used key_obj
self.__key_obj_map.append(key_obj)

View File

@@ -13,7 +13,7 @@ setup(
},
author='relikd',
url='https://github.com/relikd/lektor-groupby-plugin',
version='0.9.8',
version='1.0.0',
description='Cluster arbitrary records with field attribute keyword.',
long_description=longdesc,
long_description_content_type="text/markdown",