From 3f2fc860be4b2bcd27732fe78ffa1d1a7d7e7dd1 Mon Sep 17 00:00:00 2001 From: Matthew Maglennonon Date: Tue, 10 Oct 2017 17:11:18 +0100 Subject: [PATCH 1/4] Add support for composite slices --- prismic/fragments.py | 87 +++++++++++++++++++-- setup.py | 2 +- tests/test_prismic.py | 16 +++- tests/test_prismic_fixtures.py | 135 +++++++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 8 deletions(-) diff --git a/prismic/fragments.py b/prismic/fragments.py index 14e0a7a..907e956 100644 --- a/prismic/fragments.py +++ b/prismic/fragments.py @@ -178,8 +178,24 @@ def as_html(self, link_resolver): html.append("""
""" % key) html.append(self.fragment_to_html(fragment, link_resolver)) html.append("""
""") + return ''.join(html) + def __getitem__(self, name): + return self.fragments[name] + + def __iter__(self): + return iter(self.fragments) + + def keys(self): + return self.fragments.keys() + + def items(self): + return self.fragments.items() + + def values(self): + return self.fragments.values() + # Links class Link(FragmentElement): @@ -224,13 +240,21 @@ def __init__(self, value): def as_html(self, documentlink_resolver, html_serializer=None): """Get the DocumentLink as html. - :param documentlink_resolver: A resolver function will be called with :class:`prismic.fragments.Fragment.DocumentLink ` object as argument. Resolver function should return a string, the local url to the document. + :param documentlink_resolver: A resolver function will be called with + :class:`prismic.fragments.Fragment.DocumentLink ` object as + argument. Resolver function should return a string, the local url to the document. """ - return """%(slug)s""" % {"link": self.get_url(documentlink_resolver), "slug": self.slug} + return """%(slug)s""" % { + "link": self.get_url(documentlink_resolver), + "slug": self.slug + } def get_url(self, documentlink_resolver=None): if not hasattr(documentlink_resolver, '__call__'): - raise Exception("documentlink_resolver should be a callable object, but it's: %s" % type(documentlink_resolver)) + raise Exception( + "documentlink_resolver should be a callable object, but it's: %s" + % type(documentlink_resolver) + ) return documentlink_resolver(self) def get_document_id(self): @@ -468,6 +492,8 @@ def as_html(self, link_resolver): html.append(group_doc.as_html(link_resolver)) return "\n".join(html) + def __iter__(self): + return iter(self.value) class Slice(FragmentElement): @@ -486,6 +512,48 @@ def as_html(self, link_resolver): "body": self.value.as_html(link_resolver) } + class CompositeSlice(FragmentElement): + + def __init__(self, slice_type, slice_label, elt): + self.slice_type = slice_type + self.slice_label = slice_label + self.repeat = [] + self.non_repeat = {} + + _repeat = elt.get('repeat') + _non_repeat = elt.get('non-repeat') + + if any(_repeat): + self.repeat = self.parse_repeat(_repeat) + + if _non_repeat: + self.non_repeat = self.parse_non_repeat(_non_repeat) + + @staticmethod + def parse_repeat(repeat): + return Fragment.Group(repeat) + + @staticmethod + def parse_non_repeat(non_repeat): + return Fragment.Group([non_repeat]) + + def as_html(self, link_resolver): + classes = ['slice'] + if self.slice_label: + classes.append(self.slice_label) + + body = "" + if self.non_repeat: + body += self.non_repeat.as_html(link_resolver) + + if self.repeat: + body += self.repeat.as_html(link_resolver) + + return '
%(body)s
' % { + "slice_type": self.slice_type, + "classes": ' '.join(classes), + "body": body + } class SliceZone(FragmentElement): @@ -494,8 +562,14 @@ def __init__(self, value): for elt in value: slice_type = elt['slice_type'] slice_label = elt.get('slice_label') - fragment = Fragment.from_json(elt['value']) - self.slices.append(Fragment.Slice(slice_type, slice_label, fragment)) + + # Old style slice + if 'value' in elt: + fragment = Fragment.from_json(elt['value']) + self.slices.append(Fragment.Slice(slice_type, slice_label, fragment)) + else: + Fragment.CompositeSlice(slice_type, slice_label, elt) + self.slices.append(Fragment.CompositeSlice(slice_type, slice_label, elt)) def as_html(self, link_resolver): html = [] @@ -503,6 +577,9 @@ def as_html(self, link_resolver): html.append(slice.as_html(link_resolver)) return "\n".join(html) + def __iter__(self): + return iter(self.slices) + class StructuredText(object): diff --git a/setup.py b/setup.py index 70cdcba..d45c334 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='prismic', - version='1.4.2', + version='1.4.3', description='Prismic.io development kit', author='The Prismic.io Team', author_email='contact@prismic.io', diff --git a/tests/test_prismic.py b/tests/test_prismic.py index 6dffd67..48fea02 100644 --- a/tests/test_prismic.py +++ b/tests/test_prismic.py @@ -9,7 +9,7 @@ from prismic.exceptions import InvalidTokenError, AuthorizationNeededError, InvalidURLError from .test_prismic_fixtures import fixture_api, fixture_search, fixture_groups, \ fixture_structured_lists, fixture_empty_paragraph, fixture_store_geopoint, fixture_image_links, \ - fixture_spans_labels, fixture_block_labels, fixture_custom_html, fixture_slices + fixture_spans_labels, fixture_block_labels, fixture_custom_html, fixture_slices, fixture_composite_slices import time import json import logging @@ -37,6 +37,7 @@ def setUp(self): self.fixture_spans_labels = json.loads(fixture_spans_labels) self.fixture_custom_html = json.loads(fixture_custom_html) self.fixture_slices = json.loads(fixture_slices) + self.fixture_composite_slices = json.loads(fixture_composite_slices) self.api = prismic.Api(self.fixture_api, self.token, ShelveCache("prismictest"), None) @@ -409,7 +410,7 @@ def test_slicezone(self): self.maxDiff = 10000 doc = prismic.Document(self.fixture_slices) slices = doc.get_slice_zone("article.blocks") - slices_html =slices.as_html(PrismicTestCase.link_resolver) + slices_html = slices.as_html(PrismicTestCase.link_resolver) expected_html = ( """
""" """
c'est un bloc features
\n""" @@ -417,6 +418,16 @@ def test_slicezone(self): # Comparing len rather than actual strings because json loading is not in a deterministic order for now self.assertEqual(len(expected_html), len(slices_html)) + def test_composite_slices(self): + self.maxDiff = 1000 + doc = prismic.Document(self.fixture_composite_slices) + slices = doc.get_slice_zone("test.body") + slices_html = slices.as_html(PrismicTestCase.link_resolver) + expected_html = """

Slice A non-repeat text

Slice A non-repeat title

Repeatable title A

Repeatable text A

+

Repeatable title B

Repeatable text B

+

Slice A non-repeat title

""" + self.assertEqual(expected_html, slices_html) + def test_image_links(self): self.maxDiff = 10000 text = prismic.fragments.StructuredText(self.fixture_image_links.get('value')) @@ -516,6 +527,7 @@ def test_geopoint_near(self): .query(predicates.near('my.store.coordinates', 40.689757, -74.0451453, 15)) self.assertEqual(f.data['q'], ['[[:d = geopoint.near(my.store.coordinates, 40.689757, -74.0451453, 15)]]']) + class TestCache(unittest.TestCase): def setUp(self): diff --git a/tests/test_prismic_fixtures.py b/tests/test_prismic_fixtures.py index 6523edd..09c54f3 100644 --- a/tests/test_prismic_fixtures.py +++ b/tests/test_prismic_fixtures.py @@ -1246,3 +1246,138 @@ } } """ + +fixture_composite_slices = """ +{ + "alternate_languages": [], + "data": { + "test": { + "body": { + "type": "SliceZone", + "value": [ + { + "non-repeat": { + "non-repeat-text": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Slice A non-repeat text", + "type": "paragraph" + } + ] + }, + "non-repeat-title": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Slice A non-repeat title", + "type": "heading1" + } + ] + } + }, + "repeat": [ + { + "repeat-text": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Repeatable text A", + "type": "paragraph" + } + ] + }, + "repeat-title": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Repeatable title A", + "type": "heading1" + } + ] + } + }, + { + "repeat-text": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Repeatable text B", + "type": "paragraph" + } + ] + }, + "repeat-title": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Repeatable title B", + "type": "heading1" + } + ] + } + } + ], + "slice_label": null, + "slice_type": "slice-a", + "type": "Slice" + }, + { + "non-repeat": { + "image": { + "type": "Image", + "value": { + "main": { + "alt": null, + "copyright": null, + "dimensions": { + "height": 500, + "width": 800 + }, + "url": "https://prismic-io.s3.amazonaws.com/tails/014c1fe46e3ceaf04b7cc925b2ea7e8027dc607a_mobile_header_tp.png" + }, + "views": {} + } + }, + "title": { + "type": "StructuredText", + "value": [ + { + "spans": [], + "text": "Slice A non-repeat title", + "type": "heading1" + } + ] + } + }, + "repeat": [ + {} + ], + "slice_label": null, + "slice_type": "slice-b", + "type": "Slice" + } + ] + } + } + }, + "first_publication_date": "2017-10-10T11:30:08+0000", + "href": "http://tails.prismic.io/api/v1/documents/search?ref=WdyvQCsAAOgSj_r0&q=%5B%5B%3Ad+%3D+at%28document.id%2C+%22WdyvPCsAAOMSj_rf%22%29+%5D%5D", + "id": "WdyvPCsAAOMSj_rf", + "lang": "en-gb", + "last_publication_date": "2017-10-10T11:30:08+0000", + "linked_documents": [], + "slugs": [ + "slice-a-non-repeat-title" + ], + "tags": [], + "type": "test", + "uid": "test" +} +""" \ No newline at end of file From f8234d0d5b014f576fb202171dd8022ed49c7492 Mon Sep 17 00:00:00 2001 From: Matthew Maglennonon Date: Wed, 11 Oct 2017 11:32:46 +0100 Subject: [PATCH 2/4] fix test? --- tests/test_prismic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_prismic.py b/tests/test_prismic.py index 48fea02..fafe91d 100644 --- a/tests/test_prismic.py +++ b/tests/test_prismic.py @@ -423,8 +423,8 @@ def test_composite_slices(self): doc = prismic.Document(self.fixture_composite_slices) slices = doc.get_slice_zone("test.body") slices_html = slices.as_html(PrismicTestCase.link_resolver) - expected_html = """

Slice A non-repeat text

Slice A non-repeat title

Repeatable title A

Repeatable text A

-

Repeatable title B

Repeatable text B

+ expected_html = """

Slice A non-repeat text

Slice A non-repeat title

Repeatable text A

Repeatable title A

+

Repeatable text B

Repeatable title B

Slice A non-repeat title

""" self.assertEqual(expected_html, slices_html) From f3236b207f8cb9e6a14013b89b1afa2cea5f9728 Mon Sep 17 00:00:00 2001 From: Matthew Maglennonon Date: Wed, 11 Oct 2017 11:49:02 +0100 Subject: [PATCH 3/4] deal with json non-determinism --- tests/test_prismic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_prismic.py b/tests/test_prismic.py index fafe91d..6cfdc0d 100644 --- a/tests/test_prismic.py +++ b/tests/test_prismic.py @@ -426,7 +426,8 @@ def test_composite_slices(self): expected_html = """

Slice A non-repeat text

Slice A non-repeat title

Repeatable text A

Repeatable title A

Repeatable text B

Repeatable title B

Slice A non-repeat title

""" - self.assertEqual(expected_html, slices_html) + # Comparing len rather than actual strings because json loading is not in a deterministic order for now + self.assertEqual(len(expected_html), len(slices_html)) def test_image_links(self): self.maxDiff = 10000 From 16f33ecc43aac7987fc62478942cec28ba2427d9 Mon Sep 17 00:00:00 2001 From: Zelane Date: Wed, 11 Oct 2017 21:23:06 +0100 Subject: [PATCH 4/4] Upgraded the minor version to 1.5.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d45c334..026c29d 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( name='prismic', - version='1.4.3', + version='1.5.0', description='Prismic.io development kit', author='The Prismic.io Team', author_email='contact@prismic.io',