aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElena ``of Valhalla'' Grandi <valhalla@trueelena.org>2021-09-16 12:23:54 +0200
committerElena ``of Valhalla'' Grandi <valhalla@trueelena.org>2021-09-16 12:23:54 +0200
commitb3e0bcb8da7bea6c0b0636fdf68d0aac6ab74754 (patch)
treebbd329234a6fedd6691225364a4ba2cb26757b8e
parent6d920f6aad7f17a676883c6f3054b7b0d8be9844 (diff)
Add a method to create an entry from a rendered template
-rw-r--r--lesana/collection.py20
-rw-r--r--lesana/command.py21
-rw-r--r--tests/data/simple/templates/new_entry_from_data.yaml12
-rw-r--r--tests/data/simple/templates/new_entry_from_data_broken.yaml14
-rw-r--r--tests/data/simple/templates/new_entry_from_data_invalid_yaml.yaml13
-rw-r--r--tests/data/simple/templates/new_entry_from_multiple_data.yaml12
-rw-r--r--tests/test_collection.py71
7 files changed, 150 insertions, 13 deletions
diff --git a/lesana/collection.py b/lesana/collection.py
index 41dd23f..20f8a0c 100644
--- a/lesana/collection.py
+++ b/lesana/collection.py
@@ -523,6 +523,26 @@ class Collection(object):
raise TemplatingError('Could not find template ' + str(e))
return template
+ def entry_from_rendered_template(self, template, data):
+ try:
+ template = self.get_template(template)
+ rendered = template.render(**data)
+ except jinja2.exceptions.TemplateSyntaxError as e:
+ raise TemplatingError(e)
+ try:
+ data = ruamel.yaml.load(rendered, ruamel.yaml.RoundTripLoader)
+ except ruamel.yaml.YAMLError as e:
+ logger.warning(
+ "The following data failed to load as YAML: \n{}".format(
+ rendered
+ )
+
+ )
+ raise TemplatingError(e)
+ entry = self.entry_class(self, data=data)
+ self.save_entries([entry])
+ return entry
+
@classmethod
def init(
cls, directory=None, git_enabled=True, edit_file=None, settings={}
diff --git a/lesana/command.py b/lesana/command.py
index 0521234..4c723af 100644
--- a/lesana/command.py
+++ b/lesana/command.py
@@ -4,8 +4,6 @@ import os
import subprocess
import sys
-import ruamel.yaml
-
from . import Collection, Entry, TemplatingError
logger = logging.getLogger(__name__)
@@ -420,22 +418,19 @@ class Export(Command):
collection.start_search(' '.join(self.args.query))
results = collection.get_all_search_results()
for entry in results:
+ data = {
+ "entry": entry
+ }
+ data.update(entry.data)
try:
- template = collection.get_template(self.args.template)
- rendered = template.render(entry=entry, **entry.data)
+ destination.entry_from_rendered_template(
+ self.args.template,
+ data
+ )
except TemplatingError as e:
logger.error("Error converting entry: {}".format(entry))
logger.error("{}".format(e))
sys.exit(1)
- try:
- data = ruamel.yaml.load(rendered, ruamel.yaml.RoundTripLoader)
- except ruamel.yaml.YAMLError as e:
- logger.error("Error loading exported entry: {}".format(entry))
- logger.error("exported data was\n{}".format(rendered))
- logger.error("{}".format(e))
- sys.exit(1)
- e = self.entry_class(destination, data=data)
- destination.save_entries([e])
class Init(Command):
diff --git a/tests/data/simple/templates/new_entry_from_data.yaml b/tests/data/simple/templates/new_entry_from_data.yaml
new file mode 100644
index 0000000..1f9e9a5
--- /dev/null
+++ b/tests/data/simple/templates/new_entry_from_data.yaml
@@ -0,0 +1,12 @@
+name: '{{ name }}'
+description: |
+ {{ description if description else "." | indent(width=2, first=False) }}
+position: '{{ position }}'
+# # quantity (integer): how many items are there
+quantity: {{ quantity if quantity else "0" }}
+# # value (float): how much each item is
+value: 0.0
+# # cost (decimal): how much this costs
+cost: '0'
+# # other (yaml):
+other:
diff --git a/tests/data/simple/templates/new_entry_from_data_broken.yaml b/tests/data/simple/templates/new_entry_from_data_broken.yaml
new file mode 100644
index 0000000..73798ce
--- /dev/null
+++ b/tests/data/simple/templates/new_entry_from_data_broken.yaml
@@ -0,0 +1,14 @@
+name: '{{ name }}'
+description: |
+ {{ description if description else "." indent(width=2, first=False) }}
+ # Note that the lack of | between "." and indent is wanted, to get a proper
+ # TemplatingError.
+position: '{{ position }}'
+# quantity (integer): how many items are there
+quantity: {{ quantity if quantity else "0" }}
+# value (float): how much each item is
+value: 0.0
+# cost (decimal): how much this costs
+cost: '0'
+# other (yaml):
+other:
diff --git a/tests/data/simple/templates/new_entry_from_data_invalid_yaml.yaml b/tests/data/simple/templates/new_entry_from_data_invalid_yaml.yaml
new file mode 100644
index 0000000..7493947
--- /dev/null
+++ b/tests/data/simple/templates/new_entry_from_data_invalid_yaml.yaml
@@ -0,0 +1,13 @@
+# A : in that position should result in invalid yaml
+name: '{{ name }}': some text
+description: |
+ {{ description if description else "." | indent(width=2, first=False) }}
+position: '{{ position }}'
+# # quantity (integer): how many items are there
+quantity: {{ quantity if quantity else "0" }}
+# # value (float): how much each item is
+value: 0.0
+# # cost (decimal): how much this costs
+cost: '0'
+# # other (yaml):
+other:
diff --git a/tests/data/simple/templates/new_entry_from_multiple_data.yaml b/tests/data/simple/templates/new_entry_from_multiple_data.yaml
new file mode 100644
index 0000000..cfa4296
--- /dev/null
+++ b/tests/data/simple/templates/new_entry_from_multiple_data.yaml
@@ -0,0 +1,12 @@
+name: '{{ data.name }}'
+description: |
+ {{ description if description else "." | indent(width=2, first=False) }}
+position: '{{ data.position }}'
+# # quantity (integer): how many items are there
+quantity: {{ values.quantity if values.quantity else "0" }}
+# # value (float): how much each item is
+value: 0.0
+# # cost (decimal): how much this costs
+cost: '{{ values.cost if values.cost else "0.0" }}'
+# # other (yaml):
+other:
diff --git a/tests/test_collection.py b/tests/test_collection.py
index d05415f..2308633 100644
--- a/tests/test_collection.py
+++ b/tests/test_collection.py
@@ -1,4 +1,5 @@
import datetime
+import decimal
import logging
import os.path
import shutil
@@ -294,6 +295,76 @@ class testSimpleCollection(unittest.TestCase):
{'value': None, 'frequency': 1},
])
+ def test_entry_from_template(self):
+ # TODO: make finding the templates less prone to breaking and
+ # then remove the cwd change from here
+ old_cwd = os.getcwd()
+ os.chdir(self.tmpdir)
+ data = {
+ "name": "This is a name",
+ }
+ entry = self.collection.entry_from_rendered_template(
+ "templates/new_entry_from_data.yaml",
+ data
+ )
+ os.chdir(old_cwd)
+ self.assertIsInstance(entry, lesana.Entry)
+ self.assertEqual(entry.data["name"], "This is a name")
+
+ def test_entry_from_template_multiple_data_sources(self):
+ # TODO: make finding the templates less prone to breaking and
+ # then remove the cwd change from here
+ old_cwd = os.getcwd()
+ os.chdir(self.tmpdir)
+ data = {
+ "name": "This is a name",
+ }
+ values = {
+ "quantity": 5,
+ "cost": decimal.Decimal("3.5"),
+ }
+ entry = self.collection.entry_from_rendered_template(
+ "templates/new_entry_from_multiple_data.yaml",
+ {
+ "data": data,
+ "values": values
+ }
+ )
+ os.chdir(old_cwd)
+ self.assertIsInstance(entry, lesana.Entry)
+ self.assertEqual(entry.data["name"], "This is a name")
+ self.assertEqual(entry.data["quantity"], 5)
+
+ def test_entry_from_bad_template(self):
+ # TODO: make finding the templates less prone to breaking and
+ # then remove the cwd change from here
+ old_cwd = os.getcwd()
+ os.chdir(self.tmpdir)
+ data = {
+ "name": "This is a name",
+ }
+ with self.assertRaises(lesana.collection.TemplatingError):
+ self.collection.entry_from_rendered_template(
+ "templates/new_entry_from_data_broken.yaml",
+ data
+ )
+ os.chdir(old_cwd)
+
+ def test_entry_from_bad_yaml(self):
+ # TODO: make finding the templates less prone to breaking and
+ # then remove the cwd change from here
+ old_cwd = os.getcwd()
+ os.chdir(self.tmpdir)
+ data = {
+ "name": "This is a name",
+ }
+ with self.assertRaises(lesana.collection.TemplatingError):
+ self.collection.entry_from_rendered_template(
+ "templates/new_entry_from_data_invalid_yaml.yaml",
+ data
+ )
+ os.chdir(old_cwd)
+
class testComplexCollection(unittest.TestCase):
def setUp(self):