예제 #1
0
파일: socrates.py 프로젝트: strogo/socrates
 def init_template_renderer(self):
     """
     Create and save a reference to an instance of a template renderer
     """
     t = self.SETTINGS["templates"]
     layout = os.path.join(self.ROOT, "layout")
     if t == "jinja2":
         self.template = Jinja2Renderer(layout)
     elif t == "django":
         self.template = DjangoRenderer(layout)
     else:
         raise NotImplementedError("I don't know this template type.")
예제 #2
0
파일: socrates.py 프로젝트: strogo/socrates
class Generator(object):
    """
    This is the main class of the application. It handles the collection,
    processing and rendering of the site.
    """

    # Templates
    SINGLE = "single.html"
    PAGE = "page.html"
    INDEX = "index.html"
    CATEGORY = "category.html"
    ARCHIVE = "archive.html"
    PAGED = "index_paged.html"
    ATOM = "atom.html"
    SITEMAP = "sitemap.xml"

    # Directories
    ROOT = None
    DEPLOY = None
    POSTS = None
    PAGES = None

    # Global, site-wide settings
    SETTINGS = None

    def __init__(self, directory, silent=False):
        m = os.getcwd()
        self.ROOT = os.path.join(m, directory)
        self.silent = silent

        if not os.path.exists(self.ROOT):
            sys.stderr.write("The '%s' directory doesn't exist.\n" % directory)
            return
        # Set up deploy directory
        self.DEPLOY = os.path.join(self.ROOT, "deploy")
        if not os.path.exists(self.DEPLOY):
            os.mkdir(self.DEPLOY)
        # Copy media files to deploy destination
        if os.path.exists(os.path.join(self.DEPLOY, "media")):
            shutil.rmtree(os.path.join(self.DEPLOY, "media"))
        shutil.copytree(os.path.join(self.ROOT, "layout", "media"), os.path.join(self.DEPLOY, "media"))

        self.POSTS = os.path.join(self.ROOT, "posts")
        self.PAGES = os.path.join(self.ROOT, "pages")

        self.SETTINGS = self._get_settings()
        if self.SETTINGS["text_processor"] not in AVAILABLE_EXTENSIONS:
            ext = self.SETTINGS["text_processor"]
            sys.stderr("WARNING: %s isn't a recognizeed text processor.\n" % ext)
            sys.exit(1)
        self.init_template_renderer()

        self.posts = []
        self.pages = []
        self.categories = {}
        self.years = {}
        self.archives = {}

        self.load_posts()
        self.process_posts()

        if os.path.exists(self.PAGES):
            self.load_pages()
            self.process_pages()

        self.SETTINGS = dict(self.SETTINGS, **{"categories": self.categories})
        self.SETTINGS = dict(self.SETTINGS, **{"years": self.years})

        # Create html files
        self.make_index_page()
        self.make_atom()
        self.make_sitemap()

        if not self.SETTINGS["skip_categories"]:
            self.make_category_pages()
        if not self.SETTINGS["skip_archives"]:
            self.make_archive_pages()

        self.make_pagination()

        if not self.SETTINGS["inline_css"]:
            from pygments.formatters import HtmlFormatter

            formatter = HtmlFormatter(**self.SETTINGS["pygments"])
            css_content = formatter.get_style_defs(".highlight")
            self._write_to_file(os.path.join(self.DEPLOY, "media", "pygments.css"), css_content)

        self.log("Success!")

    def init_template_renderer(self):
        """
        Create and save a reference to an instance of a template renderer
        """
        t = self.SETTINGS["templates"]
        layout = os.path.join(self.ROOT, "layout")
        if t == "jinja2":
            self.template = Jinja2Renderer(layout)
        elif t == "django":
            self.template = DjangoRenderer(layout)
        else:
            raise NotImplementedError("I don't know this template type.")

    def render(self, template, values):
        """
        Give a template name and a dict of values, return an HTML
        representation of a page/post.
        """
        return self.template.render(template, values)

    def log(self, text):
        """
        A thin wrapper around the ``print`` statement to allow silencing of all
        non-error output.
        """
        if not self.silent:
            print text

    def _get_page_str(self, current, total):
        """
        Return page number as a string for pagination.
        """
        current += 1
        if total < 10:
            return str(current)
        elif 10 <= total < 100:
            if total < 10:
                return "0%d" % current
            else:
                return str(current)

    def _get_post_cats(self, post):
        """
        Extract and save post's categories
        """
        cats = post.config.get("categories", ["Uncategorized"])
        for c in cats:
            if c not in self.categories:
                self.categories[c] = [post]
            else:
                self.categories[c].append(post)

    def _get_settings(self):
        """
        Read main config file. Return dict.
        """
        s = os.path.join(self.ROOT, "config.yaml")
        if not os.path.exists(s):
            raise Exception("No config file.")
        c = open(s, "r").read()
        return dict(DEFAULTS, **yaml.load(c))

    def _write_to_file(self, path, contents):
        """
        Create a file (path) with contents
        """
        f = open(path, "w")
        f.write(contents.encode("utf-8"))
        f.close()

    def _v(self, vals):
        """
        Create a joint dict of global config and local template vals
        """
        return dict(self.SETTINGS, **vals)

    def load_posts(self):
        """
        Get all files from the posts directory, create Post instances and add
        them to the self.posts list.
        """
        for filename in os.listdir(self.POSTS):
            if not filename.startswith(".") and not filename.startswith("_"):
                p = os.path.join(self.POSTS, filename)
                try:
                    self.posts.append(Post(p, self.SETTINGS))
                except ConfigurationError:
                    sys.stderr.write("WARNING: %s isn't configured properly.\n" % filename)
                    sys.exit(1)
        self.posts.reverse()

    def load_pages(self):
        """
        Get all files from the posts directory, create Post instances and add
        them to the self.posts list.
        """
        for filename in os.listdir(self.PAGES):
            if not filename.startswith(".") and not filename.startswith("_"):
                p = os.path.join(self.PAGES, filename)
                try:
                    self.pages.append(Page(p, self.SETTINGS))
                except ConfigurationError:
                    sys.stderr.write("WARNING: %s isn't configured properly.\n" % filename)
                    sys.exit(1)

    def process_posts(self):
        """
        Collect all the necessary information about posts.
        """
        self.log("Processing posts...")
        for post in self.posts:
            # Get categories
            self._get_post_cats(post)

            # Post dirs
            #   years
            date = post.config["date"]
            year = str(date.year)
            if year not in self.years:
                self.years[year] = {}
            #   months
            m = date.strftime("%m")
            y = self.years[year]
            if m not in y.keys():
                self.years[year][m] = []

            if self.SETTINGS["url_include_day"]:
                if post.day not in self.years[year][m]:
                    self.years[year][m].append(post.day)

            # Archives
            d = year
            if d not in self.archives:
                self.archives[d] = [post]
            else:
                self.archives[d].append(post)

        self.make_post_directories()
        self.save_posts()

    def process_pages(self):
        self.save_pages()

    def save_posts(self):
        self.log("Saving posts...")
        for post in self.posts:
            # Save the thing
            b = post.slug + ".html"

            if self.SETTINGS["url_include_day"]:
                m = os.path.join(self.DEPLOY, post.year, post.month, post.day, b)
            else:
                m = os.path.join(self.DEPLOY, post.year, post.month, b)

            if "template" in post.config.keys():
                t = post.config["template"]
            else:
                t = self.SINGLE

            content = self.render(t, self._v({"post": post}))

            # Print filename to show progress
            self.log(post.filename)

            self._write_to_file(m, content)

    def save_pages(self):
        self.log("Saving pages...")
        for page in self.pages:
            # Save the thing
            b = page.slug + ".html"

            m = os.path.join(self.DEPLOY, b)

            if "template" in page.config.keys():
                t = page.config["template"]
            else:
                t = self.PAGE

            content = self.render(t, self._v({"page": page}))

            # Print filename to show progress
            self.log(page.filename)
            self._write_to_file(m, content)

    def make_post_directories(self):
        """
        If they don't exist, create post directories.
            2011
                01
                  04
                  05
                  12
                03
                  09
                  11
                12
                  21
            2010
                04
                  11
                07
                  30
        """
        self.log("Creating directories...")
        keys = self.years.keys()
        for k in keys:
            m = os.path.join(self.DEPLOY, k)
            if not os.path.exists(m):
                os.mkdir(m)

            months = self.years[k]
            for month in months:
                m = os.path.join(self.DEPLOY, k, month)
                if not os.path.exists(m):
                    os.mkdir(m)

                if self.SETTINGS["url_include_day"]:
                    days = self.years[k][month]
                    for day in days:
                        m = os.path.join(self.DEPLOY, k, month, day)
                        if not os.path.exists(m):
                            os.mkdir(m)

    def make_index_page(self):
        """
        Create an index page
        """
        m = os.path.join(self.DEPLOY, "index.html")
        if self.SETTINGS["posts_per_page"] == 0:
            # List all posts on the index page
            posts = self.posts
            extra = False
        elif len(self.posts) > self.SETTINGS["posts_per_page"]:
            # Cut off unnecessary posts
            n = len(self.posts)
            n = self.SETTINGS["posts_per_page"]
            posts = self.posts[:n]
            extra = True
        else:
            posts = self.posts
            extra = False
        contents = self.render(self.INDEX, self._v({"posts": posts, "extra": extra}))
        self._write_to_file(m, contents)

    def make_sitemap(self):
        """
        Generate a sitemap.
        """
        m = os.path.join(self.DEPLOY, "sitemap.xml")
        posts = self.posts
        contents = self.render(self.SITEMAP, self._v({"posts": posts, "now": self._get_atom_date()}))
        self._write_to_file(m, contents)

    def make_atom(self):
        """
        Create an atom feed
        """
        m = os.path.join(self.DEPLOY, "atom.xml")
        if self.SETTINGS["posts_per_page"] == 0:
            # List all posts on the index page
            posts = self.posts
        elif len(self.posts) > self.SETTINGS["posts_per_page"]:
            # Cut off unnecessary posts
            n = len(self.posts)
            n = self.SETTINGS["posts_per_page"]
            posts = self.posts[:n]
        else:
            posts = self.posts
        contents = self.render(self.ATOM, self._v({"posts": posts, "now": self._get_atom_date()}))
        self._write_to_file(m, contents)

    def _get_atom_date(self):
        date = datetime.utcnow()
        d = date.strftime("%Y-%m-%dT%H:%M:%S%z")
        return d + "Z"

    def make_category_pages(self):
        """
        Make category pages. They go into the 'category' directory.
        """
        keys = self.categories.keys()
        if len(keys) != 0:
            # Make category dir
            self.CATEGORIES = os.path.join(self.DEPLOY, "category")
            if not os.path.exists(self.CATEGORIES):
                os.mkdir(self.CATEGORIES)
            for k in keys:
                p = os.path.join(self.CATEGORIES, slugify(k))
                if not os.path.exists(p):
                    os.mkdir(p)
                posts = self.categories[k]
                contents = self.render(self.CATEGORY, self._v({"category": k, "posts": posts}))
                m = os.path.join(p, "index.html")
                self._write_to_file(m, contents)

    def make_archive_pages(self):
        """
        Make archive pages. Only by year.
        """
        self.log("Creating archives...")
        keys = self.archives.keys()
        if len(keys) != 0:
            # Make category dir
            self.ARCHIVES = os.path.join(self.DEPLOY, "archive")
            if not os.path.exists(self.ARCHIVES):
                os.mkdir(self.ARCHIVES)
            for k in keys:
                p = os.path.join(self.ARCHIVES, slugify(k))
                if not os.path.exists(p):
                    os.mkdir(p)
                posts = self.archives[k]
                contents = self.render(self.ARCHIVE, self._v({"year": k, "posts": posts}))
                m = os.path.join(p, "index.html")
                self._write_to_file(m, contents)

    def make_pagination(self):
        """
        Make pagination.
            /page/2/
            /page/3/
        Uses posts per page setting
        """
        per = int(self.SETTINGS["posts_per_page"])
        if per == 0:
            # Skip pagination if all posts are on index page
            return
        self.log("Creating pagination...")
        num = len(self.posts)
        pages = num / per

        m = os.path.join(self.DEPLOY, "page")
        if not os.path.exists(m):
            os.mkdir(m)
        for x in range(1, pages + 1):
            if x == 1:
                prev = "/"
            else:
                prev = "/page/%d/" % x

            if x < pages:
                next = "/page/%d/" % int(x + 1)
            else:
                next = None

            n = per * x
            p = self.posts[n : n + per]

            v = {"page": x + 1, "posts": p, "prev": prev, "total": pages + 1, "next": next}

            folder = self._get_page_str(x, pages + 1)

            e = os.path.join(m, folder)
            if not os.path.exists(e):
                os.mkdir(e)

            contents = self.render(self.PAGED, self._v(v))
            c = os.path.join(e, "index.html")
            self._write_to_file(c, contents)