Skip to content
This repository has been archived by the owner on Apr 18, 2020. It is now read-only.

Commit

Permalink
Subclassed the metadata deserializer for more rational structure
Browse files Browse the repository at this point in the history
  • Loading branch information
palewire committed Jul 5, 2017
1 parent adeff8d commit 77d23b0
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 82 deletions.
6 changes: 3 additions & 3 deletions bigbuild/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
from greeking import latimes_ipsum
from django.test import RequestFactory
from bigbuild import context_processors
from bigbuild.serializers import deserializers
from django.template import Engine, RequestContext
from django.utils.encoding import python_2_unicode_compatible
from bigbuild.serializers import BigBuildFrontmatterDeserializer
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -146,8 +146,8 @@ def refresh_from_db(self):
"""
Reads in the frontmatter from metadata.yaml and syncs it with the object.
"""
deserializer = BigBuildFrontmatterDeserializer(self.slug, self.__class__.__name__)
yaml_obj = deserializer.deserialize()
deserializer = deserializer = deserializers[self.__class__.__name__]()
yaml_obj = deserializer.deserialize(self.slug)
for field in yaml_obj._meta.fields:
setattr(self, field.name, getattr(yaml_obj, field.name))
self.data_objects = yaml_obj.data_objects or {}
Expand Down
6 changes: 3 additions & 3 deletions bigbuild/models/pagelists.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import bigbuild
from django.conf import settings
from collections import Sequence
from bigbuild.serializers import deserializers
from bigbuild.serializers import BigBuildJSONDeserializer
from bigbuild.serializers import BigBuildFrontmatterDeserializer
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -79,8 +79,8 @@ def get_page(slug, pagetype):
return None

# Create a Page object from the directory slug
deserializer = BigBuildFrontmatterDeserializer(slug, pagetype)
return deserializer.deserialize()
deserializer = deserializers[pagetype]()
return deserializer.deserialize(slug)

def get_dynamic_pages(self):
"""
Expand Down
179 changes: 103 additions & 76 deletions bigbuild/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,91 +113,118 @@ def is_metadata_valid(self, metadata):
return False


class BigBuildFrontmatterDeserializer(object):
class BaseBigBuildFrontmatterDeserializer(object):
"""
Given the page to a YAML deserialize it from Jekyll's frontmatter format.
"""
def __init__(self, slug, model_name='Page'):
def set_metadata(self, obj):
stream = open(obj.frontmatter_path, 'r')
post = frontmatter.load(stream)

# Set the basic frontmatter metadata
obj.headline = post.metadata['headline']
obj.byline = post.metadata['byline']
obj.description = post.metadata['description']
obj.image_url = post.metadata['image_url']
obj.pub_date = post.metadata['pub_date']
obj.published = post.metadata['published']
obj.show_in_feeds = post.metadata['show_in_feeds']

# Attach any extra stuff
try:
obj.extra = post.metadata['extra']
except KeyError:
obj.extra = {}

try:
obj.data = post.metadata['data']
except KeyError:
obj.data = {}

# Pull in the content as is
obj.content = post.content

# Render it out as flat HTML
obj.content = obj.rendered_content

# Make sure the page has recommended metadata
# ... if it's ready to publish
if obj.pub_status in ['live', 'pending']:
if not obj.has_recommended_metadata():
logger.warn(MissingRecommendedMetadataWarning(obj))

# Pass it back out
return obj

def deserialize(self, slug):
logger.debug("Retrieving {} as {} object".format(
slug,
model_name
self.model.__name__
))
self.slug = slug
self.model_name = model_name
self.model = apps.get_app_config('bigbuild').get_model(model_name)

def deserialize(self):
obj = self.model.create(slug=self.slug, skip_create_directory=True)
obj = self.model.create(slug=slug, skip_create_directory=True)
try:
stream = open(obj.frontmatter_path, 'r')
post = frontmatter.load(stream)

# Set the basic frontmatter metadata
obj.headline = post.metadata['headline']
obj.byline = post.metadata['byline']
obj.description = post.metadata['description']
obj.image_url = post.metadata['image_url']
obj.pub_date = post.metadata['pub_date']
obj.published = post.metadata['published']
obj.show_in_feeds = post.metadata['show_in_feeds']

# Attach any extra stuff
try:
obj.extra = post.metadata['extra']
except KeyError:
obj.extra = {}

try:
obj.data = post.metadata['data']
except KeyError:
obj.data = {}

# Pull in the content as is
obj.content = post.content

# Render it out as flat HTML
obj.content = obj.rendered_content

# Make sure the page has recommended metadata
# ... if it's ready to publish
if obj.pub_status in ['live', 'pending']:
if not obj.has_recommended_metadata():
logger.warn(MissingRecommendedMetadataWarning(obj))

# Extra stuff if this is a live Page model
if self.model_name == 'Page':
# Loop through any data files
for key, path in post.metadata.get('data', {}).items():

# Generate the path if it's stored in the default `data` directory
data_dir = os.path.join(obj.page_directory_path, 'data')
p = os.path.join(data_dir, path)
# If it doesn't exist, see if it's in another folder
if not os.path.exists(p):
p = os.path.join(obj.page_directory_path, path)
# If it's not there either, throw an error
if not os.path.exists(p):
logging.debug("Data file could not be found at %s" % p)

# Open the file
with codecs.open(p, 'r') as f:
# If it's a CSV file open it that way...
if p.endswith(".csv"):
obj.data_objects[key] = list(csv.DictReader(f))
# If it's a JSON file open it this way ...
elif p.endswith(".json"):
obj.data_objects[key] = json.load(f)
# If it's a YAML file open it t'other way ...
elif (p.endswith(".yml") or p.endswith(".yaml")):
obj.data_objects[key] = yaml.load(f)
elif p.endswith(".aml"):
obj.data_objects[key] = archieml.load(f)
# If it's none of those throw an error.
else:
logging.debug("Data file at %s not recognizable type" % path)

# Sync the metadata from the YAML frontmatter with the object
obj = self.set_metadata(obj)
# Pass it out
return obj
except Exception as e:
# Map to deserializer error
six.reraise(DeserializationError, DeserializationError(e), sys.exc_info()[2])


class PageFrontmatterDeserializer(BaseBigBuildFrontmatterDeserializer):

def __init__(self):
self.model = apps.get_app_config('bigbuild').get_model('Page')

def set_metadata(self, obj):
obj = super(PageFrontmatterDeserializer, self).set_metadata(obj)

# Reopen the YAML frontmatter
stream = open(obj.frontmatter_path, 'r')
post = frontmatter.load(stream)

# Loop through any data files
for key, path in post.metadata.get('data', {}).items():

# Generate the path if it's stored in the default `data` directory
data_dir = os.path.join(obj.page_directory_path, 'data')
p = os.path.join(data_dir, path)
# If it doesn't exist, see if it's in another folder
if not os.path.exists(p):
p = os.path.join(obj.page_directory_path, path)
# If it's not there either, throw an error
if not os.path.exists(p):
logging.debug("Data file could not be found at %s" % p)

# Open the file
with codecs.open(p, 'r') as f:
# If it's a CSV file open it that way...
if p.endswith(".csv"):
obj.data_objects[key] = list(csv.DictReader(f))
# If it's a JSON file open it this way ...
elif p.endswith(".json"):
obj.data_objects[key] = json.load(f)
# If it's a YAML file open it t'other way ...
elif (p.endswith(".yml") or p.endswith(".yaml")):
obj.data_objects[key] = yaml.load(f)
elif p.endswith(".aml"):
obj.data_objects[key] = archieml.load(f)
# If it's none of those throw an error.
else:
logging.debug("Data file at %s not recognizable type" % path)

# Pass it back out
return obj


class ArchivedPageFrontmatterDeserializer(BaseBigBuildFrontmatterDeserializer):

def __init__(self):
self.model = apps.get_app_config('bigbuild').get_model('ArchivedPage')


deserializers = {
'Page': PageFrontmatterDeserializer,
'ArchivedPage': ArchivedPageFrontmatterDeserializer
}

0 comments on commit 77d23b0

Please sign in to comment.