update example readme v0.9.3
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
# Usage
|
||||
|
||||
Overview:
|
||||
- the [quick config](#quick-config) example shows how you can use the plugin config to setup a quick and easy tagging system.
|
||||
- the [simple example](#simple-example) goes into detail how this plugin works.
|
||||
- the [advanced example](#advanced-example) touches on the potentials of the plugin.
|
||||
- [quick config example](#quick-config) 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.
|
||||
- [Misc](#misc) shows other use-cases.
|
||||
|
||||
|
||||
|
||||
@@ -45,9 +46,16 @@ The easiest way to add tags to your site is by defining the `groupby.ini` config
|
||||
```ini
|
||||
[testA]
|
||||
root = /
|
||||
slug = config/{group}.html
|
||||
slug = config/{key}.html
|
||||
template = example-config.html
|
||||
split = ' '
|
||||
enabled = True
|
||||
|
||||
[testA.fields]
|
||||
title = "Tagged: " ~ this.group
|
||||
|
||||
[testA.key_map]
|
||||
Blog = News
|
||||
```
|
||||
|
||||
The configuration parameter are:
|
||||
@@ -57,9 +65,11 @@ The configuration parameter are:
|
||||
All results will be placed under this directory, e.g., `/tags/tagname/`.
|
||||
If you use `root = /blog`, the results path will be `/blog/tags/tagname/`.
|
||||
The groupby plugin will traverse all sub-pages wich contain the attribute `testA`.
|
||||
3. The `slug` parameter (`config/{group}.html`) is where the results are placed.
|
||||
3. The `slug` parameter (`config/{key}.html`) is where the results are placed.
|
||||
In our case, the path resolves to `config/tagname.html`.
|
||||
The default value is `{attrib}/{group}/index.html` which would resolve to `testA/tagname/index.html`.
|
||||
The default value is `{attrib}/{key}/index.html` which would resolve to `testA/tagname/index.html`.
|
||||
If this field contains `{key}`, it just replaces the value with the group-key.
|
||||
In all other cases the field value is evaluated in a jinja context.
|
||||
4. The `template`parameter (`example-config.html`) is used to render the results page.
|
||||
If no explicit template is set, the default template `groupby-testA.html` will be used.
|
||||
Where `testA` is replaced with whatever attribute you chose.
|
||||
@@ -68,18 +78,54 @@ The configuration parameter are:
|
||||
The split is only relevant for fields of type `string` or `text`.
|
||||
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.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
In your template file you have access to the children (pages) and their tags.
|
||||
The emitted `extras` for the child is a list of original tagnames.
|
||||
There are two additional config mappings, `.fields` and `.key_map`.
|
||||
Key-value pairs in `.fields` 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.
|
||||
|
||||
The built-in field attributes are:
|
||||
|
||||
- `group`: returned group name, e.g., "A Title?"
|
||||
- `key`: slugified group value, e.g., "a-title"
|
||||
- `slug`: url path after root node, e.g. "config/a-title.html" (can be `None`)
|
||||
- `record`: parent node, e.g., `Page(path="/")`
|
||||
- `children`: dictionary of `{record: extras}` pairs
|
||||
- `first_child`: first page
|
||||
- `first_extra`: first extra
|
||||
- `config`: configuration object (see below)
|
||||
|
||||
Without any changes, the `key` value will just be `slugify(group)`.
|
||||
However, the other mapping `.key_map` will replace `group` with whatever replacement value is provided in the `.key_map` and then slugified.
|
||||
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(group))`.
|
||||
|
||||
The `config` attribute contains the values that created the group:
|
||||
|
||||
- `key`: attribute key, e.g., `TestA`
|
||||
- `root`: as provided by init, e.g., `/`
|
||||
- `slug`: the raw value, e.g., `config/{key}.html`
|
||||
- `template`: as provided by init, e.g., `example-config.html`
|
||||
- `enabled`: boolean
|
||||
- `dependencies`: path to config file (if initialized from config)
|
||||
- `fields`: raw values from `TestA.fields`
|
||||
- `key_map`: raw values from `TestA.key_map`
|
||||
|
||||
In your template file you have access to the attributes, config, and children (pages):
|
||||
|
||||
```jinja2
|
||||
{%- for child, extras in this.children.items() %}
|
||||
<li>Page: {{ child.path }}, Tags: {{ extras }}</li>
|
||||
<h2>{{ this.title }}</h2>
|
||||
<p>Key: {{ this.key }}, Attribute: {{ this.config.key }}</p>
|
||||
<ul>
|
||||
{%- for child in this.children %}
|
||||
<li>Page: {{ child.path }}</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
```
|
||||
|
||||
|
||||
@@ -93,60 +139,64 @@ templates/example-simple.html
|
||||
```
|
||||
|
||||
```python
|
||||
def on_groupby_after_build_all(self, groupby, builder, **extra):
|
||||
@groupby.watch('/blog', 'testB', slug='simple/{group}/index.html',
|
||||
template='example-simple.html', flatten=True)
|
||||
def convert_simple_example(args):
|
||||
value = args.field # list, since model is 'strings' type
|
||||
for tag in value:
|
||||
yield slugify(tag), {'val': tag, 'tags_in_page': len(value)}
|
||||
def on_groupby_before_build_all(self, groupby, builder, **extra):
|
||||
watcher = groupby.add_watcher('testB', {
|
||||
'root': '/blog',
|
||||
'slug': 'simple/{key}/index.html',
|
||||
'template': 'example-simple.html',
|
||||
})
|
||||
watcher.config.set_key_map({'Foo': 'bar'})
|
||||
watcher.config.set_fields({'date': datetime.now()})
|
||||
|
||||
# page = args.record # extract additional info from source
|
||||
# fieldKey, flowIndex, flowKey = args.key # or get field index
|
||||
# if flowIndex is None:
|
||||
# obj = page[fieldKey]
|
||||
# else:
|
||||
# obj = page[fieldKey].blocks[flowIndex].get(flowKey)
|
||||
@watcher.grouping(flatten=True)
|
||||
def convert_simple_example(args):
|
||||
# Yield groups
|
||||
value = args.field # type: list # since model is 'strings' type
|
||||
for tag in value:
|
||||
yield tag, {'tags_in_page': value}
|
||||
```
|
||||
|
||||
This example is roughly equivalent to the config file example.
|
||||
The parameters of the `@groupby.watch` function (`root`, `attribute`, `slug`, `template`) correspond to the same config parameters described above.
|
||||
There is a new `flatten` parameter:
|
||||
This example is roughly equivalent to the config example above – the parameters of the `groupby.add_watcher` function correspond to the same config parameters.
|
||||
Additionally, you can set other types in `set_fields` (all strings are evaluated in jinja context!).
|
||||
|
||||
- Flatten determines how Flow elements are processed.
|
||||
`@watcher.grouping` sets the callback to generate group keys.
|
||||
It has one optional flatten parameter:
|
||||
|
||||
- `flatten` determines how Flow elements are processed.
|
||||
If `False`, the callback function is called once per Flow element.
|
||||
If `True` (default), the callback is called for all Flow-Blocks of the Flow individually.
|
||||
The attribute `testB` can be attached to either the Flow or a Flow-Block regardless.
|
||||
|
||||
The `args` parameter of the `convert_simple_example()` function is a named tuple, it has three attributes:
|
||||
|
||||
1. The `record` points to the `Page` source which contains the tag.
|
||||
1. The `record` points to the `Page` record that contains the tag.
|
||||
2. The `key` tuple `(field-key, flow-index, flow-key)` tells which field is processed.
|
||||
For Flow types, `flow-index` and `flow-key` are set, otherwise they are `None`.
|
||||
3. The `field` value is the content of the processed field.
|
||||
The field value is reoughly equivalent to the following:
|
||||
The field value is equivalent to the following:
|
||||
|
||||
```python
|
||||
args.page[fieldKey].blocks[flowIndex].get(flowKey)
|
||||
k = args.key
|
||||
field = args.record[k.fieldKey].blocks[k.flowIndex].get(k.flowKey)
|
||||
```
|
||||
|
||||
The callback body **can** produce groupings but does not have to.
|
||||
If you choose to produce an entry, you have to `yield` a tuple pair of `(groupkey, extra-info)`.
|
||||
`groupkey` is used to combine & cluster pages and must be URL-safe.
|
||||
The `extra-info` is passed through to your template file.
|
||||
You can yield more than one entry per source or filter / ignore pages if you don't yield anything.
|
||||
If you choose to produce an entry, you have to `yield` a string or tuple pair `(group, extra-info)`.
|
||||
`group` is slugified (see above) and then used to combine & cluster pages.
|
||||
The `extra-info` (optional) is passed through to your template file.
|
||||
You can yield more than one entry per source.
|
||||
Or ignore pages if you don't yield anything.
|
||||
|
||||
The template file can access and display the `extra-info`:
|
||||
|
||||
```jinja2
|
||||
{%- for child, extras in this.children.items() %}
|
||||
<b>Page: {{ child.title }}<b>
|
||||
<p>Custom field date: {{this.date}}</p>
|
||||
<ul>
|
||||
{%- for extra in extras %}
|
||||
<li>Name: {{ extra.val }}, Tag count: {{ extra.tags_in_page }}</li>
|
||||
{%- for child, extras in this.children.items() -%}
|
||||
{%- set etxra = (extras|first).tags_in_page %}
|
||||
<li>{{etxra|length}} tags on page "{{child.path}}": {{etxra}}</li>
|
||||
{%- endfor %}
|
||||
</ul>
|
||||
{%- endfor %}
|
||||
```
|
||||
|
||||
|
||||
@@ -166,44 +216,72 @@ Except that it loads a config file and replaces in-text occurrences of `{{Tagnam
|
||||
```python
|
||||
def on_groupby_before_build_all(self, groupby, builder, **extra):
|
||||
# load config
|
||||
regex = re.compile(self.get_config().get('match'))
|
||||
# since we load and use a config file, we need to track the dependency
|
||||
@groupby.depends_on(self.config_filename)
|
||||
@groupby.watch('/', 'testC', slug='advanced/{group}/',
|
||||
template='example-advanced.html')
|
||||
config = self.get_config()
|
||||
regex = config.get('testC.pattern.match')
|
||||
try:
|
||||
regex = re.compile(regex)
|
||||
except Exception as e:
|
||||
print('inlinetags.regex not valid: ' + str(e))
|
||||
return
|
||||
|
||||
# load config directly (which also tracks dependency)
|
||||
watcher = groupby.add_watcher('testC', config)
|
||||
|
||||
@watcher.grouping()
|
||||
def convert_replace_example(args):
|
||||
# args.field assumed to be Markdown
|
||||
obj = args.field.source
|
||||
slugify_map = {} # type Dict[str, str]
|
||||
for match in regex.finditer(obj):
|
||||
tag = match.group(1)
|
||||
yield slugify(tag), tag
|
||||
key = yield tag
|
||||
print('[advanced] slugify:', tag, '->', key)
|
||||
slugify_map[tag] = key
|
||||
|
||||
def _fn(match: re.Match) -> str:
|
||||
tag = match.group(1)
|
||||
return f'<a href="/advanced/{slugify(tag)}/">{tag}</a>'
|
||||
return f'<a href="/advanced/{slugify_map[tag]}/">{tag}</a>'
|
||||
args.field.source = regex.sub(_fn, obj)
|
||||
```
|
||||
|
||||
One **important** thing to notice is, we use `on_groupby_before_build_all` to register our callback function.
|
||||
This is required because we would like to modify the source **before** it is written to disk.
|
||||
If you look back to the [simple example](#simple-example), we used `on_groupby_after_build_all` because we did not care when it is executed.
|
||||
Generally, it makes little difference which one you use (`on-after` is likely less busy).
|
||||
Just know that you can process the source before or after it is build.
|
||||
Notice, `add_watcher` accepts a config file as parameter which keeps also track of dependencies and rebuilds pages when you edit the config file.
|
||||
Further, the `yield` call returns the slugified group-key.
|
||||
First, you do not need to slugify it yourself and second, potential replacements from `key_map` are already handled.
|
||||
|
||||
For Markdown fields, we can modify the `source` attribute directly.
|
||||
All other field typed need to be accessed via `args.record` key indirection.
|
||||
All other field types need to be accessed via `args.record` key indirection (see [simple example](#simple-example)).
|
||||
|
||||
```ini
|
||||
[testC]
|
||||
root = /
|
||||
slug = "advanced/{}/".format(this.key)
|
||||
template = example-advanced.html
|
||||
|
||||
[testC.pattern]
|
||||
match = {{([^}]{1,32})}}
|
||||
```
|
||||
|
||||
Lastly, the config file contains a regular expression which matches `{{` + any string less than 32 characters + `}}`.
|
||||
Notice, the parenthesis (`()`) will match the inner part but the replace function (`re.sub`) will remove the `{{}}` too.
|
||||
The config file takes the same parameters as the [config example](#quick-config).
|
||||
As you can see, `slug` is evaluated in jinja context.
|
||||
|
||||
If the user changes the regex pattern in the config file, we need to rebuild all tags.
|
||||
For this purpose we need to track changes to the config file.
|
||||
This is done by calling:
|
||||
We introduced 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 but the replace function (`re.sub`) will remove the `{{}}`.
|
||||
|
||||
```python
|
||||
@groupby.depends_on(file1, file2, ...)
|
||||
|
||||
|
||||
## Misc
|
||||
|
||||
It was shortly mentioned above that slugs can be `None` (only if manually set to `slug = None`).
|
||||
This is useful if you do not want to create subpages but rather an index page containing all groups.
|
||||
This can be done in combination with the next use-case:
|
||||
|
||||
```jinja2
|
||||
{%- for x in this|vgroups('TestA', 'TestB', recursive=True)|unique|sort %}
|
||||
<a href="{{ x|url }}">({{ x.group }})</a>
|
||||
{%- endfor %}
|
||||
```
|
||||
|
||||
You can query the groups of any parent node (including those without slug).
|
||||
The keys (`'TestA', 'TestB'`) can be omitted which will return all groups of all attributes (you can still filter them with `x.config.key == 'TestC'`).
|
||||
Refer to `templates/page.html` for usage.
|
||||
|
||||
Reference in New Issue
Block a user