allow setting fields other than 'text' in tiddlers
The interface isn't the cleanest (it'd probably be better to map from tiddlers to a set of fields, rather than having a different set of mappings for each field), but it will do for now. Needing more than a few of these is a Zettelkasten-design smell anyway.
This commit is contained in:
parent
5065efd37e
commit
197ec6c8b2
117
tzk/builders.py
117
tzk/builders.py
@ -10,6 +10,7 @@ time. This allows the configuration file to be read at any time to retrieve
|
|||||||
information about the defined products without actually running any build steps.
|
information about the defined products without actually running any build steps.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Mapping
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import functools
|
import functools
|
||||||
import json
|
import json
|
||||||
@ -19,7 +20,7 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Callable, Dict, List, Set, Sequence, Tuple
|
from typing import Callable, Dict, List, Optional, Set, Sequence, Tuple
|
||||||
|
|
||||||
from tzk import git
|
from tzk import git
|
||||||
from tzk import tw
|
from tzk import tw
|
||||||
@ -419,10 +420,83 @@ def replace_private_people(initialer: Callable[[str], str] = None) -> None:
|
|||||||
f.writelines(lines)
|
f.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
@tzk_builder
|
def _set_fields(mappings: Dict[str, str],
|
||||||
def set_tiddler_values(mappings: Dict[str, str]) -> None:
|
editing_func: Callable[[Path, List[str], str], None]) -> None:
|
||||||
"""
|
"""
|
||||||
Set the ``text`` field of selected config or other tiddlers to arbitrary new values.
|
Read the text of a .tid file into memory and call an editing_func on it
|
||||||
|
in order to modify some of its fields.
|
||||||
|
|
||||||
|
:param mappings: Mapping of tiddler filenames to new values of some field
|
||||||
|
(this function is field-agnostic; the editing_func should be
|
||||||
|
aware of what field is to be edited).
|
||||||
|
:param editing_func: A function which will make the changes.
|
||||||
|
|
||||||
|
The editing_func is called once for each item in the mapping
|
||||||
|
and receives three arguments:
|
||||||
|
|
||||||
|
:param tiddler_path: The full path to the tiddler on the filesystem.
|
||||||
|
The editing_func should write changes back to this file,
|
||||||
|
if any are appropriate.
|
||||||
|
:param tiddler_lines: A sequence of strings, each one line of the original file.
|
||||||
|
:param new_text: The new field value specified for this tiddler in the provided
|
||||||
|
mapping dict of tiddler name/new value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for tiddler, new_text in mappings.items():
|
||||||
|
tiddler_path = (Path(build_state['public_wiki_folder']) / "tiddlers" / tiddler)
|
||||||
|
try:
|
||||||
|
with tiddler_path.open("r") as f:
|
||||||
|
tiddler_lines = f.readlines()
|
||||||
|
except FileNotFoundError:
|
||||||
|
stop(f"File {tiddler_path} not found. "
|
||||||
|
f"(Did you forget to end the name with '.tid'?)")
|
||||||
|
|
||||||
|
editing_func(tiddler_path, tiddler_lines, new_text)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_text_field(mappings: Dict[str, str]) -> None:
|
||||||
|
"Set the 'text' field to a new value in each pair of tiddler name/value."
|
||||||
|
def editor(tiddler_path: Path, tiddler_lines: Sequence[str], new_text: str) -> None:
|
||||||
|
if not str(tiddler_path).endswith('.tid'):
|
||||||
|
# will be a separate meta file, so the whole thing is the text field
|
||||||
|
first_blank_line_index = 0
|
||||||
|
else:
|
||||||
|
first_blank_line_index = next(idx
|
||||||
|
for idx, value in enumerate(tiddler_lines)
|
||||||
|
if not value.strip())
|
||||||
|
with tiddler_path.open("w") as f:
|
||||||
|
f.writelines(tiddler_lines[0:first_blank_line_index+1])
|
||||||
|
f.write(new_text)
|
||||||
|
_set_fields(mappings, editor)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_nontext_field(field: str, mappings: Dict[str, str]) -> None:
|
||||||
|
"Set the specified /field/ to a new value in each pair of tiddler name/value."
|
||||||
|
def editor(tiddler_path: Path, tiddler_lines: Sequence[str], new_text: str) -> None:
|
||||||
|
# First check if the field exists and is currently not set to new_text...
|
||||||
|
line_to_change = -1
|
||||||
|
for idx, line in enumerate(tiddler_lines):
|
||||||
|
if m := re.match(f"^{field}: (?P<value>.*)", line):
|
||||||
|
if m.group('value') != new_text:
|
||||||
|
line_to_change = idx
|
||||||
|
break
|
||||||
|
|
||||||
|
# ...and if so, write the file again with the change in place.
|
||||||
|
if line_to_change != -1:
|
||||||
|
with tiddler_path.open("w") as f:
|
||||||
|
for idx, line in enumerate(tiddler_lines):
|
||||||
|
if idx == line_to_change:
|
||||||
|
f.write(f"{field}: {new_text}\n")
|
||||||
|
else:
|
||||||
|
f.write(line)
|
||||||
|
_set_fields(mappings, editor)
|
||||||
|
|
||||||
|
|
||||||
|
@tzk_builder
|
||||||
|
def set_tiddler_values(text: Optional[Dict[str, str]] = None,
|
||||||
|
**kwargs: Dict[str, str]) -> None:
|
||||||
|
"""
|
||||||
|
Set fields of selected config or other tiddlers to arbitrary new values.
|
||||||
|
|
||||||
This can be used to make customizations that can't easily be done with feature
|
This can be used to make customizations that can't easily be done with feature
|
||||||
flags or other wikitext solutions within the wiki -- for instance, changing the
|
flags or other wikitext solutions within the wiki -- for instance, changing the
|
||||||
@ -436,31 +510,22 @@ def set_tiddler_values(mappings: Dict[str, str]) -> None:
|
|||||||
'$__config_sib_CurrentEditionPublicity.tid': 'public',
|
'$__config_sib_CurrentEditionPublicity.tid': 'public',
|
||||||
})
|
})
|
||||||
|
|
||||||
:param mappings: A dictionary whose keys are tiddler filenames
|
Any number of arguments can be provided. The name of each argument is the name of
|
||||||
and whose values are the values to be inserted
|
the field to edit. One positional argument may be used with no name; this argument
|
||||||
in those tiddlers' ``text`` fields.
|
is assumed to be mappings for replacing the 'text' field.
|
||||||
"""
|
"""
|
||||||
assert 'public_wiki_folder' in build_state
|
assert 'public_wiki_folder' in build_state
|
||||||
|
for field_name, field_mapping in kwargs.items():
|
||||||
|
if not isinstance(field_mapping, Mapping):
|
||||||
|
raise AssertionError(
|
||||||
|
"Arguments to set_tiddler_values must be dictionaries "
|
||||||
|
"mapping tiddler names to values.")
|
||||||
|
|
||||||
for tiddler, new_text in mappings.items():
|
if text is not None:
|
||||||
tiddler_path = (Path(build_state['public_wiki_folder']) / "tiddlers" / tiddler)
|
_set_text_field(text)
|
||||||
try:
|
|
||||||
with tiddler_path.open("r") as f:
|
for field, mappings in kwargs.items():
|
||||||
tiddler_lines = f.readlines()
|
_set_nontext_field(field, mappings)
|
||||||
except FileNotFoundError:
|
|
||||||
stop(f"File {tiddler_path} not found. "
|
|
||||||
f"(Did you forget to end the name with '.tid'?)")
|
|
||||||
|
|
||||||
if not str(tiddler_path).endswith('.tid'):
|
|
||||||
# will be a separate meta file, so the whole thing is the text field
|
|
||||||
first_blank_line_index = 0
|
|
||||||
else:
|
|
||||||
first_blank_line_index = next(idx
|
|
||||||
for idx, value in enumerate(tiddler_lines)
|
|
||||||
if not value.strip())
|
|
||||||
with tiddler_path.open("w") as f:
|
|
||||||
f.writelines(tiddler_lines[0:first_blank_line_index+1])
|
|
||||||
f.write(new_text)
|
|
||||||
|
|
||||||
|
|
||||||
@tzk_builder
|
@tzk_builder
|
||||||
|
Loading…
Reference in New Issue
Block a user