pip install -e .
+## Dev Install
+
**OPTIONAL**
This isn't needed to hack on the site.
pip install PyHyphen
-Finally, let's fix the missing dict (which I dug up from somewhere).
+If you try to use the package, it'll yell at you in an uncouth fashion:
+
+ >>> import hyphen
+ >>> hy = hyphen.Hyphenator('en_US')
+ Traceback (most recent call last):
+ ...
+ IOError: Cannot load hyphen dictionary.
+
+Bitches. Okay, let's fix the missing dict. Since it's not available on
+OpenOffice.org any more, I had to dig up a copy from a random sourceforge
+project -- it's `etc/hyph_en_US.dic`. The package expects it in its source
+root:
cp etc/hyph_en_US.dic .env/lib/python2.7/site-packages/hyphen/
+Done! Now everything should work fine.
+
+ >>> import hyphen
+ >>> hy = hyphen.Hyphenator('en_US')
+ >>> hy.syllables(u'autopoietic')
+ [u'au', u'topoi', u'et', u'ic']
+ >>> hy.syllables(u'ion')
+ []
+
+It works! (okok. It's not perfect, but it's good enough.)
## Features
### Pages
- **Home**
- - Best Of / Staff Picks
- - Popular
- - Mentioned on Fb/Twitter (etc)
- - Longest Chains?
-- **Haiku Page**
- - Unique URL per haiku (plus short URL)
- - Tags, context, favs, ratings
+ - Best Of / Staff Picks (this is `haikus-fav.txt`)
+ - Popular (calc'd using likes, mentions, views)
+ - Longest Chains
+ - Mentions (on fb/twitter)
+
+- **Haiku Pages**
+ - Haiku text + report context, link to location in report
+ - Unique URL (+short URL)
+ - Like, Tag
- Comments ("share your story"?)
- - Sharing (tweet this, share on fb, AddThis)
+ - Share (AddThis, tweet this, share on Fb)
- Mentions (on twitter/fb)
-- **Report**
- - Split out by-chapter
- - Haikus highlighted inline in report, link to Haiku Page
- - Per-line comments?
+
+- **The Report**
+ - TOC w/ page per chapter/section; can deep link to paragraphs
+ - Haikus highlighted inline (w/ link to Haiku Page)
+ - Comments, Likes per- paragraph/section/chapter
+ - Share (AddThis, tweet this, share on Fb)
+
- **Search**
- - Fulltext of haikus
- - Fulltext of report (by chapter)
- - By tag
+ - Fulltext of all Haikus
+ - Fulltext of Report (by chapter & paragraph)
+ - Browse by Tag
+
- **Users**
- - Favorites, Rate, Comment, Tag
- - Signup required to mutate (w/ connect via FB, Twitter, GitHub, Google)
+ - OAuth: Fb, Twitter, GitHub
+ - OpenID: Google
+ - Likes, Comments, Tags
+
- **Download**
- Zips of the haikus, report
- Source on GitHub
-### Models
-
-- User
-- Haiku
-- Like
-- Text
-- Comment
-
## Debugging Notes
-When running `ipython` under `virtualenv`, you can activate the environment from within the interpreter:
+### ipython and virtualenv
+
+`ipython` under `virtualenv` does not seem to recursively add the non-zipped packages
+in `.env/lib/python2.7/site-packages` (whereas the normal python repl does). Thankfully,
+you can activate the environment from within the interpreter:
````python
execfile('.env/bin/activate_this.py', dict(__file__='.env/bin/activate_this.py'))
````
-Which resolves problems with the env's `site-packages` dir not getting picked up recursively.
+You can do this automatically with a little extra work on startup.
-To do this automatically in ipython, I do a little work on startup. In my `ipython_config.py` file,
-in `c.TerminalIPythonApp.exec_lines` I have (among many other things) an import for
-a module `dsc_ipython` where I keep all my ipython specific code, including:
+I have done this work.
+
+In my `ipython_config.py` file, I have `c.TerminalIPythonApp.exec_lines` import
+a module where I keep all my ipython specific code (`dsc_ipython`), including:
````python
+### Automatically Find and Activate VirtualEnv (if present)
+
import os, sys
from os.path import isfile, join, relpath, abspath
# automatically check on startup, using defaults
activate_virtual_env()
-
````
+I also register this as the Magic command `%activate_virtual_env` (which took forever
+to figure out how to do in ipython 0.11 because the docs are totally inadequate, even if
+ipython is one of the best things since bees).
## Notes
- Did you know that the Financial Crisis Inquiry Report increased the US Gross National Haiku Quotient by 1.8%, the largest single increase every affected by a congressional report?
-- Split out Haiku-finder into its own package? That'd be neat.
+- Split out Haiku-finder into its own PyPi package? That'd be neat.
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from crisishaiku import app
+app.run(debug=True)
# -*- coding: utf-8 -*-
""" crisishaiku.com -- a website dedicated to the poetry of financial disaster
"""
-
__version__ = '0.0.1'
VERSION = tuple(map(int, __version__.split('.')))
# Load any overrides from the file specified in the env var FLASK_SETTINGS
app.config.from_envvar('FLASK_SETTINGS', silent=True)
+# render .jade files using pyjade
+app.jinja_env.add_extension('pyjade.ext.jinja.PyJadeExtension')
+
db = SQLAlchemy(app)
crypt = Bcrypt(app)
-# pw_hash = crypt.generate_password_hash('hunter2')
-# crypt.check_password_hash(pw_hash, 'hunter2') # returns True
-
oauth = OAuth()
twitter = oauth.remote_app('twitter',
base_url = 'https://api.twitter.com/1/',
request_token_url = 'https://api.twitter.com/oauth/request_token',
access_token_url = 'https://api.twitter.com/oauth/access_token',
- authorize_url = 'https://api.twitter.com/oauth/authenticate', # /autorize if we wanted to read/write
+ authorize_url = 'https://api.twitter.com/oauth/authenticate', # /authorize if we want read/write to their stream
consumer_key = app.config['TWITTER_CONSUMER_KEY'],
- consumer_secret = app.config['TWITTER_CONSUMER_SECRET']
+ consumer_secret = app.config['TWITTER_CONSUMER_SECRET'],
)
-# import crisishaiku.models
-# import crisishaiku.routes
+import crisishaiku.models
+import crisishaiku.views
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from crisishaiku import app
+app.run(debug=True)
from crisishaiku import db
+# pw_hash = crypt.generate_password_hash('hunter2')
+# crypt.check_password_hash(pw_hash, 'hunter2') # returns True
+### Helpers
+
class JSONData(TypeDecorator):
"Type representing serialized JSON data."
impl = VARCHAR
mtime = Column(DateTime, default=func.now())
-# join tables
-
-# likes = db.Table('likes',
-# Column('user_id', Integer, ForeignKey('user.id')),
-# Column('target_id', Integer, ForeignKey('target.id')),
-# )
-
-# tags2haikus = db.Table('tags',
-# Column('tag_id', Integer, ForeignKey('tag.id')),
-# Column('haiku_id', Integer, ForeignKey('haiku.id'))
-# )
+### Model Classes
class Target(db.Model):
"A polymorphic pointer to a Comment, Haiku, or Section."
- id = Column(Integer, primary_key=True)
+ id = Column(Integer, primary_key=True)
+ section = relationship('Section')
+ haiku = relationship('Haiku')
+ comment = relationship('Comment')
+
+ @property
+ def value(self):
+ return self.section or self.haiku or self.comment
class Section(db.Model):
id = Column(Integer, primary_key=True)
title = Column(String(200))
text = Column(Text())
- parent = relationship('Section', backref=backref('children', lazy='dynamic'))
+ parent_id = Column(Integer, ForeignKey('section.id'))
+ children = relationship('Section', lazy='dynamic', backref=backref('parent', lazy='dynamic'))
+ haikus = relationship('Haiku', lazy='dynamic', backref=backref('context', lazy='dynamic'))
class Haiku(db.Model):
- id = Column(Integer, primary_key=True)
- text = Column(Text())
- context = relationship('Section', backref=backref('haikus' lazy='dynamic'))
- start = Column(Integer) # character index into the section where this haiku begins
+ id = Column(Integer, primary_key=True)
+ text = Column(Text())
+ start = Column(Integer) # character index into the section where this haiku begins
+ context_id = Column(Integer, ForeignKey('section.id'))
+# likes = db.Table('likes',
+# Column('user_id', Integer, ForeignKey('user.id')),
+# Column('target_id', Integer, ForeignKey('target.id')),
+# )
+
class Like(db.Model):
id = Column(Integer, primary_key=True)
- user = relationship('User', backref=backref('likes', lazy='dynamic'))
- target = relationship('Target')
+ target = relationship('Target', lazy='dynamic')
+ user_id = Column(Integer, ForeignKey('user.id'))
ctime = Column(DateTime, default=func.now())
class Comment(TimestampMixin, db.Model):
id = Column(Integer, primary_key=True)
- target = relationship('Target')
- author = relationship('User', backref=backref('comments', lazy='dynamic'))
+ target = relationship('Target', lazy='dynamic')
+ author_id = Column(Integer, ForeignKey('user.id'))
text = Column(Text())
- approved = Column(Boolean) # spam filter approval
- deleted = Column(Boolean)
+ approved = Column(Boolean, default=False) # spam filter approval
+ deleted = Column(Boolean, default=False)
+haiku_tags = db.Table('haiku_tags',
+ Column('tag_id', Integer, ForeignKey('tag.id')),
+ Column('haiku_id', Integer, ForeignKey('haiku.id')),
+)
+
class Tag(db.Model):
id = Column(Integer, primary_key=True)
name = Column(String(120), unique=True)
- haikus = relationship('Haiku', secondary=tags2haikus, backref=backref('tags', lazy='dynamic'))
+ haikus = relationship('Haiku', secondary=haiku_tags, lazy='dynamic', backref=backref('tags', lazy='dynamic'))
+
# user_tags = db.Table('user_tags',
+# Column('tag', String(120), unique=True),
# Column('user_id', Integer, ForeignKey('user.id')),
-# Column('tag_id', Integer, ForeignKey('tag.id')),
# Column('haiku_id', Integer, ForeignKey('haiku.id')),
# )
pwhash = Column(String(60))
last_seen = Column(DateTime, default=func.now())
- verified = Column(Boolean)
- banned = Column(Boolean)
- deleted = Column(Boolean)
+ verified = Column(Boolean, default=False)
+ banned = Column(Boolean, default=False)
+ deleted = Column(Boolean, default=False)
# OAuth & OpenID accounts
accounts = Column(JSONData())
# We can store the verification crap elsewhere
# verification_token = Column(String(60))
- # comments
- # likes = relationship('Target', secondary=likes, backref=backref('tags', lazy='dynamic'))
+ likes = relationship('Like', lazy='dynamic', backref=backref('user', lazy='dynamic'))
+ comments = relationship('Comment', lazy='dynamic', backref=backref('author', lazy='dynamic'))
- # tags = relationship('UserTag')
+ # disable per-user tag tracking
+ ## tags = relationship('UserTag', backref=backref('user', lazy='dynamic'))
def __init__(self, username, email):
+++ /dev/null
-from flask import (
- request, session, url_for, redirect, abort,
- render_template, g, flash, send_from_directory, )
-from crisishaiku import app, db, crypt, twitter, models
-from path import path
-
-STATIC = path(app.root_path)/'static'
-
-
-@app.route('/favicon.ico')
-def favicon():
- return send_from_directory(STATIC, 'favicon.ico', mimetype='image/vnd.microsoft.icon')
-
-
-@app.route('/login')
-def login():
- return twitter.authorize(callback=url_for('oauth_authorized',
- next=request.args.get('next') or request.referrer or None))
-
-
-@app.route('/oauth-authorized')
-@twitter.authorized_handler
-def oauth_authorized(res):
- next_url = request.args.get('next') or url_for('index')
- if res is None:
- flash(u'You denied the request to sign in.')
- return redirect(next_url)
-
- session['twitter_token'] = (
- res['oauth_token'],
- res['oauth_token_secret']
- )
- session['twitter_user'] = res['screen_name']
-
- flash('You were signed in as %s' % res['screen_name'])
- return redirect(next_url)
-
-from crisishaiku import app
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from flask import (
+ request, session, url_for, redirect, abort,
+ render_template, g, flash, send_from_directory, )
+from path import path
+
+from crisishaiku import app, db, crypt, twitter, models
+
+STATIC = path(app.root_path)/'static'
+
+
@app.route('/')
def index():
return 'Hello World!'
+
+@app.route('/favicon.ico')
+def favicon():
+ return send_from_directory(STATIC, 'favicon.ico', mimetype='image/vnd.microsoft.icon')
+
+
+@app.route('/login')
+def login():
+ return twitter.authorize(callback=url_for('oauth_authorized',
+ next=request.args.get('next') or request.referrer or None))
+
+
+@app.route('/oauth-authorized')
+@twitter.authorized_handler
+def oauth_authorized(res):
+ next_url = request.args.get('next') or url_for('index')
+ if res is None:
+ flash(u'You denied the request to sign in.')
+ return redirect(next_url)
+
+ session['twitter_token'] = (
+ res['oauth_token'],
+ res['oauth_token_secret']
+ )
+ session['twitter_user'] = res['screen_name']
+
+ flash('You were signed in as %s' % res['screen_name'])
+ return redirect(next_url)
+
install_requires = [
# 'PyHyphen >= 1.0beta1',
- 'path >= 2.2',
+ 'path.py >= 2.2',
'bunch >= 1.0',
'jsonlib2 >= 1.5.2',
'anyjson >= 0.3.1',