Source code for posty.site

from __future__ import absolute_import

from collections import Counter
import datetime
import os.path
import shutil

from .config import Config
from .exceptions import PostyError
from .page import Page
from .post import Post
from posty.renderer import (
    HtmlRenderer,
    JsonRenderer,
    RssRenderer,
    AtomRenderer,
    Posty1RedirectRenderer
)
from .util import slugify


[docs]class Site(object): """ Representation of an entire site with posts and pages. This is the main class that conrols everything. :param site_path: Path to the directory containing site content (pages, posts, templates) :param config_path: Path to the config file, defaults to ``$SITE_PATH/config.yml`` """ def __init__(self, site_path='.', config_path=None): self.site_path = site_path if config_path: self.config_path = config_path else: self.config_path = os.path.join(site_path, 'config.yml') self._config = None self.payload = { 'pages': [], 'posts': [], 'tags': [], } self.loaded = False @property def config(self): """ Returns this site's config as read from the config file """ if not self._config: config_path = os.path.join(self.config_path) self._config = Config(config_path) self._config.load() return self._config
[docs] def init(self): """ Initialize a new Posty site at the given path """ skel_path = os.path.join(os.path.dirname(__file__), 'skel') for thing in os.listdir(skel_path): src = os.path.join(skel_path, thing) dst = os.path.join(self.site_path, thing) if os.path.exists(dst): print("{} already exists, not overwriting".format(thing)) else: if os.path.isdir(src): shutil.copytree(src, dst) elif os.path.isfile(src): shutil.copy(src, dst)
[docs] def load(self): """ Load the site from files on disk into our internal representation """ # Include the whole config # TODO: deprecate the previous items self.payload['config'] = dict(self.config) self._load_pages() self._load_posts() self.payload['copyright'] = self.copyright self.loaded = True
[docs] def render(self, output_path='build'): """ Render the site with the various renderers * HTML * JSON * RSS (if ``feeds.rss`` is True in the config) * Atom (if ``feeds.atom`` is True in the config) """ HtmlRenderer(self, output_path=output_path).render_site() JsonRenderer(self, output_path=output_path).render_site() if self.config['feeds']['rss']: RssRenderer(self, output_path=output_path).render_site() if self.config['feeds']['atom']: AtomRenderer(self, output_path=output_path).render_site() if self.config['compat']['redirect_posty1_urls']: Posty1RedirectRenderer(self, output_path=output_path).render_site()
def _load_pages(self): pages = [] page_dir = os.path.join(self.site_path, 'pages') for filename in os.listdir(page_dir): contents = open(os.path.join(page_dir, filename)).read() pages.append(Page.from_yaml(contents, config=self._config)) self.payload['pages'] = sorted(pages, key=lambda x: x['title'].lower()) def _load_posts(self): posts = [] tags = [] # Load each post post_dir = os.path.join(self.site_path, 'posts') for filename in os.listdir(post_dir): contents = open(os.path.join(post_dir, filename)).read() post = Post.from_yaml(contents, config=self._config) posts.append(post) tags.extend(post['tags']) self.payload['posts'] = sorted(posts, key=lambda x: x['date'], reverse=True) # uniquify tags and sort by frequency (descending) self.payload['tags'] = [t for t, c in Counter(tags).most_common()]
[docs] def post(self, slug): """ Returns a Post object by its slug :param slug: slug of the post to find :returns: A post dict :raises PostyError: if no post could be found """ for post in self.payload['posts']: post_slug = post['slug'] or slugify(post['title']) if slug == post_slug: return post else: raise PostyError( 'Unable to find post {}. Available posts: {}'.format( slug, [slugify(p['title']) for p in self.payload['pages']] ) )
[docs] def page(self, slug): """ Returns a Page object by its slug :param slug: slug of the page to find :returns: A page dict :raises PostyError: if no page could be found """ for page in self.payload['pages']: page_slug = page.get('slug') or slug == slugify(page['title']) if slug == page_slug: return page else: raise PostyError( 'Unable to find post {}. Available posts: {}'.format( slug, [p.get('slug') or slugify(p['title']) for p in self.payload['pages']] ) )
@property def copyright(self): """ Returns a string of the copyright info, based on the configured author and the years of the first and last post """ first_post = self.payload['posts'][-1] last_post = self.payload['posts'][0] copyright = 'Copyright {start} - {end}, {author}'.format( author=self.config['author'], start=first_post['date'].year, end=last_post['date'].year ) return copyright
[docs] def new_post(self, name="New Post"): """ Create a new post in the site directory from the skeleton post """ post_dir = os.path.join(self.site_path, 'posts') if not os.path.exists(post_dir): raise PostyError('You must initialize the site first') date = datetime.date.today() filename = '{}_{}.yaml'.format(date, slugify(name)) post_path = os.path.join(post_dir, filename) skel_path = os.path.join(os.path.dirname(__file__), 'skel/posts/1970-01-01_new-post.yaml') post = Post.from_yaml(open(skel_path).read(), config=self.config) post['title'] = name post['date'] = date with open(post_path, 'w') as output_file: output_file.write(post.to_yaml())
[docs] def new_page(self, name="New Page"): """ Create a new page in the site directory from the skeleton page """ page_dir = os.path.join(self.site_path, 'pages') if not os.path.exists(page_dir): raise PostyError('You must initialize the site first') filename = '{}.yaml'.format(slugify(name)) page_path = os.path.join(page_dir, filename) skel_path = os.path.join(os.path.dirname(__file__), 'skel/pages/new-page.yaml') page = Page.from_yaml(open(skel_path).read(), config=self.config) page['title'] = name with open(page_path, 'w') as output_file: output_file.write(page.to_yaml())