Smores

PyPI PyPI CircleCI branch Coverage Status

Smores allows you to specify a schema for user facing template features. It leverages marshmallow (hence 'smores') to populate and transform data that is then rendered via jinja. It has a parser that presents a more friendly syntax to users (ex. {user.addresses:1.street}). It also includes an autocomplete method that gives you intellisense style options given a tag fragment.

Installation

pip install smores

Smores provides two Marshmallow field types called TemplateString and TemplateFile. Templates defined in these fields are scoped to that schema and it's descendants. Each schema can have a _default_template that, if defined, will what is inserted if the associated tag ends with that schema. For example: typing {user.address} will render the _default_template for the Address schema. You can define other template attributes as well. For example, see the 'google_link' attribute of the Address schema below. Typing {user.address.google_link} will populate and insert that link.

smores.autocomplete is a method where you can provide a tag 'fragment' and it will return the possible options below that. For example:

from smores import Smores, TemplateString
from marshmallow import Schema, fields

# instantiate a smores instance
smores = Smores()

# smores.schema registers the schema with the instance
@smores.schema
class Coordinates(Schema):
    lat = fields.Decimal()
    lng = fields.Decimal()
    _default_template = TemplateString("{{lat}},{{lng}}")

@smores.schema
class Address(Schema):
    street = fields.String()
    suite = fields.String()
    city = fields.String()
    state = fields.String()
    zipcode = fields.String()
    coordinates = fields.Nested(Coordinates)
    google_link = TemplateString('<a href="https://maps.google.com/?ll={{coordinates}}">View Map</a>')
    _default_template = TemplateString("""
        <div>{{<a href="https://maps.google.com/?ll={{coordinates}}">View Map</a>}}</div>
        <div>{{street}} -- {{suite}}</div>
        <div>{{city}}, {{state}} {{zipcode}}</div>
    """)

@smores.schema
class User(Schema):
    id = fields.Integer()
    name = fields.String()
    email = fields.Email()
    address = fields.Nested(Address)
    _default_template = TemplateString("""
        <div>{{name}}</div>
        <div>E: {{email}}</div>
        <div>{{address}}</div>
    """)
# for the schemas above, simply invoke the autocomplete method with a tag fragment

>>> smores.autocomplete("")
AutocompleteResponse(tagStatus='INVALID', options=['address', 'coordinates', 'user'])

>>> smores.autocomplete('user')
AutocompleteResponse(tagStatus='VALID', options=['_default_template', 'address', 'email', 'id', 'name'])

>>> smores.autocomplete('us')
AutocompleteResponse(tagStatus='INVALID', options=['user'])

>>> smores.autocomplete("user.address.coordinates")
AutocompleteResponse(tagStatus='VALID', options=['_default_template', 'lat', 'lng'])

# Receiving '_default_template' or no results means that the current tag fragment is valid but _default_template
# shouldn't be appended to the tag in the ui.
# provide data to the render function
data = {
    "user": {
        "id": 1,
        "name": "Leanne Graham",
        "username": "Bret",
        "email": "Sincere@april.biz",
        "phone": "1-770-736-8031 x56442",
        "address": {
            "street": "Kulas Light",
            "suite": "Apt. 556",
            "city": "Gwenborough",
            "state": "MD",
            "zipcode": "92998-3874",
            "coordinates": {
                "lat": "36.065934",
                "lng": "-79.791414"
            }
        },
    }
}

# provide user created template
user_template = """
    <h3>Hi, {user.name}!</h3>
    <p>Your Info:</p>
    {user}
"""

# render the output
print smores.render(data, user_template)

# output -->
# <h3>Hi, Leanne Graham!</h3>
# <p>Your Info:</p>
# 
# <div>Leanne Graham</div>
# <div>E: Sincere@april.biz</div>
# <div>
#     <div><a href="https://maps.google.com/?ll=36.065934,-79.791414">View Map</a></div>
#     <div>Kulas Light -- Apt. 556</div>
#     <div>Gwenborough, MD 92998-3874</div>
# </div>