Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 81 additions & 130 deletions journal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,146 +7,135 @@
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.config import Configurator
from pyramid.events import NewRequest, subscriber
from pyramid.httpexceptions import HTTPFound, HTTPInternalServerError, HTTPForbidden

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for removing unused imports!

from pyramid.security import remember, forget
from pyramid.session import SignedCookieSessionFactory
from pyramid.view import view_config
from waitress import serve
from contextlib import closing
import markdown
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
scoped_session,
sessionmaker,
)
from zope.sqlalchemy import ZopeTransactionExtension

USER = 'henryhowes'

here = os.path.dirname(os.path.abspath(__file__))

DB_SCHEMA = """
CREATE TABLE IF NOT EXISTS entries (
id serial PRIMARY KEY,
title VARCHAR (127) NOT NULL,
text TEXT NOT NULL,
created TIMESTAMP NOT NULL
)
"""

INSERT_ENTRY = """INSERT INTO entries (title, text, created) VALUES (%s, %s, %s)
"""
logging.basicConfig()
log = logging.getLogger(__file__)

DB_ENTRIES_LIST = """SELECT id, title, text, created FROM entries ORDER BY created DESC
"""
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()

DB_ENTRY = """SELECT * FROM entries WHERE id=%s
"""

NEW_ENTRY = """SELECT * FROM entries ORDER BY created DESC LIMIT 1
"""
class Entry(Base):
__tablename__ = 'entries'
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
title = sa.Column(sa.Unicode(127), nullable=False)
text = sa.Column(sa.UnicodeText, nullable=False)
created = sa.Column(
sa.DateTime, nullable=False, default=datetime.datetime.utcnow
)

UPDATE_ENTRY = """UPDATE entries SET title=%s, text=%s WHERE id=%s
"""
def __repr__(self):
return u"{}: {}".format(self.__class__.__name__, self.title)

logging.basicConfig()
log = logging.getLogger(__file__)
@classmethod
def all(cls):
return DBSession.query(cls).order_by(cls.created.desc()).all()

@classmethod
def newest_entry(cls):
return DBSession.query(cls).order_by(cls.created.desc()).first()

@classmethod
def by_id(cls, id):
return DBSession.query(cls).filter(cls.id == id).one()

@classmethod
def from_request(cls, request):
title = request.params.get('title', None)
text = request.params.get('text', None)
created = datetime.datetime.utcnow()
new_entry = cls(title=title, text=text, created=created)
DBSession.add(new_entry)

def update_from_request(self, request):
self.title = request.params.get('title', None)
self.text = request.params.get('text', None)

def render_markdown(self):
return markdown.markdown(
self.text, extensions=['codehilite', 'fenced_code'])

def convert_strftime(self):
return self.created.strftime('%b %d, %Y')

def json(self):
return {'title': self.title,
'text': self.render_markdown(),
'created': self.created.strftime('%b %d, %Y'),
'id': self.id}

def json_edit_get(self):
return {'title': self.title,
'text': self.text,
'created': self.created.strftime('%b %d, %Y'),
'id': self.id}


@view_config(route_name='home', renderer='templates/list2.jinja2')
def read_entries(request):
"""return a list of all entries as dicts"""
cursor = request.db.cursor()
cursor.execute(DB_ENTRIES_LIST)
keys = ('id', 'title', 'text', 'created')
entries = [dict(zip(keys, row)) for row in cursor.fetchall()]
for item in entries:
item['text'] = markdown.markdown(
item['text'], extensions=['codehilite', 'fenced_code'])
entries = Entry.all()
return {'entries': entries}


@view_config(route_name='detail', renderer='templates/detail.jinja2')
def read_entry(request):
"""return a list of all entries as dicts"""
cursor = request.db.cursor()
cursor.execute(DB_ENTRY, (request.matchdict['id'], ))
keys = ('id', 'title', 'text', 'created')
row = cursor.fetchone()
entry = dict(zip(keys, row))
entry['text'] = markdown.markdown(
entry['text'], extensions=['codehilite', 'fenced_code'])
entry = Entry.by_id(request.matchdict['id'])
return {'entry': entry}


@view_config(route_name='edit', renderer='json')
def edit_entry_view(request):
"""return a list of all entries as dicts"""
if request.authenticated_userid:
entry = Entry.by_id(request.params.get('id', None))
if request.method == 'GET':
# import pdb; pdb.set_trace();
cursor = request.db.cursor()
cursor.execute(DB_ENTRY, (request.params.get('id', None), ))
keys = ('id', 'title', 'text', 'created')
row = cursor.fetchone()
entry = dict(zip(keys, row))
entry['created'] = entry['created'].strftime('%b %d, %Y')

return entry
return entry.json_edit_get()

elif request.method == 'POST':
# import pdb; pdb.set_trace()
try:
edit_entry(request)
entry.update_from_request(request)
except psycopg2.Error:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that you're using SQLAlchemy, the errors you might get from running update_from_request will not be psycopg2 errors. You should update this, or remove it.

# this will catch any errors generated by the database
return HTTPInternalServerError()

cursor = request.db.cursor()
cursor.execute(DB_ENTRY, (request.params.get('id', None), ))
keys = ('id', 'title', 'text', 'created')
# import pdb; pdb.set_trace()
row = cursor.fetchone()
entry = dict(zip(keys, row))

entry['text'] = markdown.markdown(
entry['text'], extensions=['codehilite', 'fenced_code'])
entry['created'] = entry['created'].strftime('%b %d, %Y')
return entry
return entry.json()
else:
return HTTPForbidden()


def write_entry(request):
"""write a single entry to the database"""
title = request.params.get('title', None)
text = request.params.get('text', None)
created = datetime.datetime.utcnow()
request.db.cursor().execute(INSERT_ENTRY, [title, text, created])


def edit_entry(request):
"""write a single entry to the database"""
title = request.params.get('title', None)
text = request.params.get('text', None)
id = request.params.get('id', None)
request.db.cursor().execute(UPDATE_ENTRY, [title, text, id])


@view_config(route_name='new', renderer='json')
def add_entry(request):
if request.authenticated_userid:
if request.method == 'POST':
try:
write_entry(request)
Entry.from_request(request)
except psycopg2.Error:
# this will catch any errors generated by the database
return HTTPInternalServerError

cursor = request.db.cursor()
cursor.execute(NEW_ENTRY)
keys = ('id', 'title', 'text', 'created')
row = cursor.fetchone()
entry = dict(zip(keys, row))
entry['text'] = markdown.markdown(
entry['text'], extensions=['codehilite', 'fenced_code'])
entry['created'] = entry['created'].strftime('%b %d, %Y')
return entry
entry = Entry.newest_entry()
return entry.json()
else:
return HTTPForbidden
return HTTPForbidden()


@view_config(route_name='login', renderer="templates/login.jinja2")
Expand Down Expand Up @@ -177,48 +166,6 @@ def logout(request):
headers=headers)


def connect_db(settings):
"""Return a connection to the configured database"""
return psycopg2.connect(settings['db'])


def init_db():
"""Create database tables defined by DB_SCHEMA

Warning: This function will not update existing table definitions
"""
settings = {}
settings['db'] = os.environ.get(
'DATABASE_URL', 'dbname=learning_journal user=henryhowes'
)
with closing(connect_db(settings)) as db:
db.cursor().execute(DB_SCHEMA)
db.commit()


@subscriber(NewRequest)
def open_connection(event):
request = event.request
settings = request.registry.settings
request.db = connect_db(settings)
request.add_finished_callback(close_connection)


def close_connection(request):
"""close the database connection for this request

If there has been an error in the processing of the request, abort any
open transactions.
"""
db = getattr(request, 'db', None)
if db is not None:
if request.exception is not None:
db.rollback()
else:
db.commit()
request.db.close()


def do_login(request):
username = request.params.get('username', None)
password = request.params.get('password', None)
Expand All @@ -237,9 +184,12 @@ def main():
settings = {}
settings['reload_all'] = os.environ.get('DEBUG', True)
settings['debug_all'] = os.environ.get('DEBUG', True)
settings['db'] = os.environ.get(
'DATABASE_URL', 'dbname=learning_journal user=henryhowes'
settings['sqlalchemy.url'] = os.environ.get(
'DATABASE_URL',
'postgresql://{}:@localhost:5432/learning_journal'.format(USER)
)
engine = sa.engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
settings['auth.username'] = os.environ.get('AUTH_USERNAME', 'admin')
manager = BCRYPTPasswordManager()
settings['auth.password'] = os.environ.get(
Expand All @@ -260,6 +210,7 @@ def main():
authorization_policy=ACLAuthorizationPolicy(),
)
config.include('pyramid_jinja2')
config.include('pyramid_tm')
config.add_static_view('static', os.path.join(here, 'static'))
config.add_route('home', '/')
config.add_route('new', '/new')
Expand Down
20 changes: 11 additions & 9 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@
Jinja2==2.7.3
Markdown==2.5.2
MarkupSafe==0.23
PasteDeploy==1.5.2
Pygments==2.0.2
WebOb==1.4
WebTest==2.0.18
argparse==1.3.0
beautifulsoup4==4.3.2
cryptacular==1.4.1
extras==0.0.3
fuzzywuzzy==0.5.0
Jinja2==2.7.3
lettuce==0.2.20
Markdown==2.5.2
MarkupSafe==0.23
mock==1.0.1
PasteDeploy==1.5.2
pbkdf2==1.3
psycopg2==2.5.4
py==1.4.26
Pygments==2.0.2
pyramid==1.5.2
pyramid-jinja2==2.3.3
pyramid-tm==0.11
pytest==2.6.4
python-mimeparse==0.1.4
python-subunit==1.0.0
repoze.lru==0.6
six==1.9.0
SQLAlchemy==0.9.8
sure==1.2.9
testtools==1.5.0
transaction==1.4.3
translationstring==1.3
unittest2==0.8.0
venusian==1.0
waitress==0.8.9
wsgiref==0.1.2
WebOb==1.4
WebTest==2.0.17
zope.deprecation==4.1.2
zope.interface==4.1.2
zope.sqlalchemy==0.7.5
38 changes: 0 additions & 38 deletions templates/base.jinja2

This file was deleted.

2 changes: 1 addition & 1 deletion templates/detail.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{% endif %}
<p class="dateline">{{ entry.created.strftime('%b. %d, %Y') }}
<div class="entry_body">
{{ entry.text|safe }}
{{ entry.render_markdown()|safe }}
</div>
<a href="https://twitter.com/share" class="twitter-share-button" data-text="{{entry.title}}" data-via="henrykhowes">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
Expand Down
Loading