Permalink
Browse files

Blogofile initial commit

  • Loading branch information...
0 parents commit 974b2a58b04269338d17e8d83379a8144686bdea @EnigmaCurry EnigmaCurry committed Mar 2, 2009
Showing with 750 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +5 −0 ERRATA
  3. +43 −0 LICENSE.txt
  4. +63 −0 blogofile/__init__.py
  5. +149 −0 blogofile/post.py
  6. +308 −0 blogofile/writer.py
  7. +43 −0 converters/wordpress2blogofile.py
  8. +115 −0 converters/wordpress_schema.py
  9. +21 −0 setup.py
@@ -0,0 +1,3 @@
+*~
+*.pyc
+*.ropeproject/*
@@ -0,0 +1,5 @@
+BeautifulSoup current doesn't work.
+BeautifulSoup==3.0.7a does.
+
+Wordpress converter outputs pages in addition to posts
+
@@ -0,0 +1,43 @@
+################################################################################
+### Blogofile is written by Ryan McGuire (EnigmaCurry.com)
+################################################################################
+
+I believe in free software. For me, this also means that I do not believe in
+copyrights, trademarks, patents, nor any other government enforced monopoly
+privilege. Accordingly, this software claims none of these privileges.
+
+You probably downloaded this software from my website. By doing so, you did not
+first enter into any sort of agreement or contract with me. There were no rules
+established before you downloaded it -- I simply offered the file on my website
+and you simply downloaded it. I am not your master -- it is immoral for me to use
+any (aggressive) force against you, including the force of government.
+
+This software is now completely yours to do with as you please.
+
+However, I am a human being. I enjoy praise, attribution, and other rewards for
+my work. If you find this software useful, I appreciate comments to that effect.
+If you find this software _very_ useful, I appreciate gifts. If you make useful
+modifications to this software, I appreciate patches. If you incorporate this
+software into a product of your own, I like to know about it, and I like being
+mentioned in your product's documentation or website. But again, I am not your
+master -- It is wrong for me to force you to do anything. I can only ask.
+
+-- Ryan McGuire, aka "EnigmaCurry"
+
+################################################################################
+### To say the same thing in legalese:
+################################################################################
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do the same.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+This is Blogofile -- http://www.Blogofile.com
+
+Definition: Blogophile --
+ A person who is fond of or obsessed with blogs or blogging.
+
+Definition: Blogofile --
+ A static file blog engine/compiler, inspired by Jekyll.
+
+Blogofile transforms a set of templates into an entire blog consisting of static
+HTML files. All categories, tags, RSS/Atom feeds are automatically maintained by
+Blogofile. This blog can be hosted on any HTTP web server. Since the blog is just
+HTML, CSS, and Javascript, no CGI environment, or database is required. With the
+addition of a of third-party comment and trackback provider (like Disqus or
+IntenseDebate) a modern and interactive blog can be hosted very inexpensively.
+
+Please take a moment to read LICENSE.txt. It's short.
+"""
+
+__author__ = "Ryan McGuire (ryan@enigmacurry.com)"
+__date__ = "Tue Feb 3 12:52:52 2009"
+__version__ = "0.1"
+
+import ConfigParser
+import os
+import sys
+
+import post
+from writer import Writer
+
+def parse_config(config_file_path):
+ return config
+
+def main():
+ from optparse import OptionParser
+ parser = OptionParser(version="Blogofile "+__version__+" -- http://www.blogofile.com")
+ parser.add_option("-c","--config-file",dest="config_file",
+ help="The config file to load (default './_config.cfg')",
+ metavar="FILE", default="./_config.cfg")
+ parser.add_option("-b","--build",dest="do_build",
+ help="Build the blog again from source",
+ default=False, action="store_true")
+ (options, args) = parser.parse_args()
+
+ #load config
+ config = ConfigParser.ConfigParser()
+ config.read(options.config_file)
+ config_dir = os.path.split(os.path.abspath(options.config_file))[0]
+ os.chdir(config_dir)
+
+ if not options.do_build:
+ parser.print_help()
+ sys.exit(1)
+
+ posts = post.parse_posts("_posts", timezone=config.get("blogofile","timezone"))
+ writer = Writer(output_dir=os.path.join(config_dir,"_site"), config=config)
+ writer.write_blog(posts)
+
+if __name__ == '__main__':
+ main()
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+post.py parses post sources from the ./_post directory.
+"""
+
+__author__ = "Ryan McGuire (ryan@enigmacurry.com)"
+__date__ = "Mon Feb 2 21:21:04 2009"
+
+import os
+import datetime
+import re
+import operator
+import urlparse
+
+import pytz
+import yaml
+import textile
+
+date_format = "%Y/%m/%d %H:%M:%S"
+
+class Post:
+ """
+ Class to describe a blog post and associated metadata
+
+ A simple post:
+
+ >>> src = '''
+ ... ---
+ ... title: First Post
+ ... date: 2008/10/20
+ ... categories: Cool Stuff , Emacs, Python, other stuff
+ ... permalink: /2008/10/20/first-post
+ ... ---
+ ...
+ ... This is a test.
+ ... '''
+ >>> p = Post(src)
+ >>> p.title
+ u'First Post'
+ >>> p.date
+ datetime.datetime(2008, 10, 20, 0, 0)
+ >>> p.categories == set([u'Cool Stuff',u'Emacs',u'Python',u'other stuff'])
+ True
+ >>> p.permalink
+ u'/2008/10/20/first-post'
+ """
+ def __init__(self, source, timezone):
+ self.source = source
+ self.yaml = yaml
+ self.title = u"Untitled - " + datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
+ self.date = datetime.datetime.now(pytz.timezone(timezone))
+ self.__timezone = timezone
+ self.updated = self.date
+ self.categories = set([u'Uncategorized'])
+ self.tags = set()
+ self.permalink = None
+ self.content = u""
+ self.draft = False
+ self.format = "textile"
+ self.author = ""
+ self.guid = None #Default guid is permalink
+ self.__parse()
+
+ def __repr__(self):
+ return "<Post title='%s' date='%s'>" % \
+ (self.title, self.date.strftime("%Y/%m/%d %H:%M:%S"))
+
+ def __parse(self):
+ """Parse the yaml and fill fields"""
+ yaml_sep = re.compile("^---$", re.MULTILINE)
+ content_parts = yaml_sep.split(self.source, maxsplit=2)
+ if len(content_parts) < 2:
+ #No yaml to extract
+ post_src = self.content
+ else:
+ #Extract the yaml at the top
+ self.__parse_yaml(content_parts[1])
+ post_src = content_parts[2]
+ #Convert post to HTML
+ if self.format == "textile":
+ self.content = textile.textile(post_src).decode("utf-8")
+ else:
+ #Assume it's raw html to begin with
+ self.content = post_src.decode("utf-8")
+
+ def __parse_yaml(self, yaml_src):
+ y = yaml.load(yaml_src)
+ try:
+ self.title = y['title']
+ except KeyError:
+ pass
+ try:
+ self.permalink = y['permalink']
+ except KeyError:
+ pass
+ try:
+ self.date = pytz.timezone(self.__timezone).localize(
+ datetime.datetime.strptime(y['date'],date_format))
+ except KeyError:
+ pass
+ try:
+ self.updated = pytz.timezone(self.__timezone).localize(
+ datetime.datetime.strptime(y['updated'],date_format))
+ except KeyError:
+ pass
+ try:
+ self.categories = set([x.strip() for x in y['categories'].split(",")])
+ except KeyError:
+ pass
+ try:
+ self.tags = set([x.strip() for x in y['tags'].split(",")])
+ except KeyError:
+ pass
+ try:
+ self.guid = y['guid']
+ except KeyError:
+ pass
+ try:
+ self.format = y['format']
+ except KeyError:
+ pass
+
+ def permapath(self):
+ """Get just the path portion of a permalink"""
+ return urlparse.urlparse(self.permalink)[2]
+
+def parse_posts(directory, timezone):
+ """Retrieve all the posts from the directory specified.
+
+ Returns a list of the posts sorted in reverse by date."""
+ posts = []
+ textile_files = [f for f in os.listdir(directory) if f.endswith(".textile")]
+ for texi in textile_files:
+ src = open(os.path.join(directory,texi)).read()
+ p = Post(src, timezone)
+ #Exclude some posts
+ if not (p.draft == True or p.permalink == None):
+ posts.append(p)
+ posts.sort(key=operator.attrgetter('date'), reverse=True)
+ return posts
+
+
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod(verbose=True)
+
Oops, something went wrong.

0 comments on commit 974b2a5

Please sign in to comment.