From: dsc Date: Sun, 25 Dec 2011 06:29:14 +0000 (-0800) Subject: Adds server script; fixes some model ... misunderstands. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=f87690ab9bbb02e8dd3aa43e3242134e077fa03d;p=crisishaiku.git Adds server script; fixes some model ... misunderstands. --- diff --git a/README.md b/README.md index 0099bb7..ebc7c4b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Then install the egg: pip install -e . +## Dev Install + **OPTIONAL** This isn't needed to hack on the site. @@ -29,10 +31,31 @@ about the missing dictionaries. 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 @@ -40,56 +63,63 @@ Finally, let's fix the missing dict (which I dug up from somewhere). ### 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 @@ -128,15 +158,17 @@ def activate_virtual_env(base=None, dirnames=None, override=False): # 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. diff --git a/bin/serve.py b/bin/serve.py new file mode 100755 index 0000000..d96a0b8 --- /dev/null +++ b/bin/serve.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from crisishaiku import app +app.run(debug=True) diff --git a/crisishaiku/__init__.py b/crisishaiku/__init__.py index 507b9f5..15a2a30 100644 --- a/crisishaiku/__init__.py +++ b/crisishaiku/__init__.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- """ crisishaiku.com -- a website dedicated to the poetry of financial disaster """ - __version__ = '0.0.1' VERSION = tuple(map(int, __version__.split('.'))) @@ -23,23 +22,23 @@ app.config.from_pyfile('../etc/flask.cfg') # 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 diff --git a/crisishaiku/__main__.py b/crisishaiku/__main__.py new file mode 100755 index 0000000..d96a0b8 --- /dev/null +++ b/crisishaiku/__main__.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from crisishaiku import app +app.run(debug=True) diff --git a/crisishaiku/models.py b/crisishaiku/models.py index 23bce30..f8e0067 100644 --- a/crisishaiku/models.py +++ b/crisishaiku/models.py @@ -15,8 +15,12 @@ import anyjson as json 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 @@ -36,22 +40,19 @@ class TimestampMixin(object): 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): @@ -59,40 +60,53 @@ 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')), # ) @@ -104,9 +118,9 @@ class User(TimestampMixin, db.Model): 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()) @@ -114,10 +128,11 @@ class User(TimestampMixin, db.Model): # 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): diff --git a/crisishaiku/routes.py b/crisishaiku/routes.py deleted file mode 100644 index 8886645..0000000 --- a/crisishaiku/routes.py +++ /dev/null @@ -1,37 +0,0 @@ -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) - diff --git a/crisishaiku/views.py b/crisishaiku/views.py index b92c26a..11706a1 100644 --- a/crisishaiku/views.py +++ b/crisishaiku/views.py @@ -1,6 +1,47 @@ -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) + diff --git a/setup.py b/setup.py index 6be6077..7ee4d6c 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setup( install_requires = [ # 'PyHyphen >= 1.0beta1', - 'path >= 2.2', + 'path.py >= 2.2', 'bunch >= 1.0', 'jsonlib2 >= 1.5.2', 'anyjson >= 0.3.1',