Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
dc90537
Copied necessary files from learning journal basic, copied routes and…
CCallahanIV Dec 22, 2016
dd686b3
Merge pull request #1 from CCallahanIV/step3
CCallahanIV Dec 22, 2016
7d39013
Updated model for LJ specific data, implemented initialization of DB,…
CCallahanIV Dec 22, 2016
c8672cb
Completed views for update and create. Preparing to write tests.
CCallahanIV Dec 22, 2016
9819cfc
Merge pull request #2 from CCallahanIV/step3
CCallahanIV Dec 22, 2016
71503ab
Added procfile.
CCallahanIV Dec 22, 2016
3d6d97c
Added requirements.txt
CCallahanIV Dec 22, 2016
0567eba
troubleshooting deployment.
CCallahanIV Dec 23, 2016
f7f9f78
Procfile i guess
CCallahanIV Dec 23, 2016
b15bcc2
Heroku is weird
CCallahanIV Dec 23, 2016
3fbf118
Added entry, hardcoded to DB intialization. Added escaping on the bo…
CCallahanIV Dec 23, 2016
aff70ba
Merge branch 'step3' of https://github.com/CCallahanIV/Learning-Journ…
CCallahanIV Dec 23, 2016
bc16591
Set up Postgres, ready for heroku deployment.
CCallahanIV Dec 24, 2016
cb8249e
Merge pull request #3 from CCallahanIV/step4
CCallahanIV Dec 24, 2016
11d9cc2
Removing DB initialization from initialize DB.
CCallahanIV Dec 27, 2016
18fae80
Updating tests to accomodate Postgres DB.
CCallahanIV Dec 30, 2016
0085c98
Added security features using environmental variables to authorize a …
CCallahanIV Jan 3, 2017
ddc0249
Fixed incorrect path reroute.
CCallahanIV Jan 3, 2017
adac736
Merge pull request #5 from CCallahanIV/security
CCallahanIV Jan 3, 2017
0ee7845
Added CSRF security.
CCallahanIV Jan 3, 2017
cff5500
Merge pull request #6 from CCallahanIV/security
CCallahanIV Jan 3, 2017
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
3 changes: 3 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[run]
source = learning_journal
omit = learning_journal/test*
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ __pycache__/
*.py[cod]
*$py.class

.DS_Store

# C extensions
*.so

Expand All @@ -23,6 +25,7 @@ var/
*.egg-info/
.installed.cfg
*.egg
*.sqlite

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0.0
---

- Initial version
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include *.txt *.ini *.cfg *.rst
recursive-include learning_journal *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: ./run
14 changes: 14 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
learning_journal README
==================

Getting Started
---------------

- cd <directory containing this file>

- $VENV/bin/pip install -e .

- $VENV/bin/initialize_learning_journal_db development.ini

- $VENV/bin/pserve development.ini

71 changes: 71 additions & 0 deletions development.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html
###

[app:main]
use = egg:learning_journal

pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar

; sqlalchemy.url = sqlite:///%(here)s/learning_journal.sqlite
; sqlalchemy.url = postgres://CCallahanIV@localhost:5432/learning_journal

# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1

###
# wsgi server configuration
###

[server:main]
use = egg:waitress#main
host = 127.0.0.1
port = 6543

###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html
###

[loggers]
keys = root, learning_journal, sqlalchemy

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console

[logger_learning_journal]
level = DEBUG
handlers =
qualname = learning_journal

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither. (Recommended for production systems.)

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
15 changes: 15 additions & 0 deletions learning_journal/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import os
from pyramid.config import Configurator


def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
settings["sqlalchemy.url"] = os.environ["DATABASE_URL"]
config = Configurator(settings=settings)
config.include('pyramid_jinja2')
config.include('.models')
config.include('.routes')
config.include('.security')
config.scan()
return config.make_wsgi_app()
73 changes: 73 additions & 0 deletions learning_journal/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
import zope.sqlalchemy

# import or define all models here to ensure they are attached to the
# Base.metadata prior to any initialization routines
from .mymodel import Entries # noqa

# run configure_mappers after defining all of the models to ensure
# all relationships can be setup
configure_mappers()


def get_engine(settings, prefix='sqlalchemy.'):
return engine_from_config(settings, prefix)


def get_session_factory(engine):
factory = sessionmaker()
factory.configure(bind=engine)
return factory


def get_tm_session(session_factory, transaction_manager):
"""
Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.

This function will hook the session to the transaction manager which
will take care of committing any changes.

- When using pyramid_tm it will automatically be committed or aborted
depending on whether an exception is raised.

- When using scripts you should wrap the session in a manager yourself.
For example::

import transaction

engine = get_engine(settings)
session_factory = get_session_factory(engine)
with transaction.manager:
dbsession = get_tm_session(session_factory, transaction.manager)

"""
dbsession = session_factory()
zope.sqlalchemy.register(
dbsession, transaction_manager=transaction_manager)
return dbsession


def includeme(config):
"""
Initialize the model for a Pyramid app.

Activate this setup using ``config.include('learning_journal.models')``.

"""
settings = config.get_settings()

# use pyramid_tm to hook the transaction lifecycle to the request
config.include('pyramid_tm')

session_factory = get_session_factory(get_engine(settings))
config.registry['dbsession_factory'] = session_factory

# make request.dbsession available for use in Pyramid
config.add_request_method(
# r.tm is the transaction manager used by pyramid_tm
lambda r: get_tm_session(session_factory, r.tm),
'dbsession',
reify=True
)
16 changes: 16 additions & 0 deletions learning_journal/models/meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import MetaData

# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
# difficult. See: http://alembic.zzzcomputing.com/en/latest/naming.html
NAMING_CONVENTION = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
}

metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
17 changes: 17 additions & 0 deletions learning_journal/models/mymodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from sqlalchemy import (
Column,
Index,
Integer,
Unicode,
Date
)

from .meta import Base


class Entries(Base):
__tablename__ = 'entries'
id = Column(Integer, primary_key=True)
title = Column(Unicode)
creation_date = Column(Date)
body = Column(Unicode)
9 changes: 9 additions & 0 deletions learning_journal/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def includeme(config):
"""Routes for learning journal web app."""
config.add_static_view(name='static', path='static', cache_max_age=3600)
config.add_route("home", "/")
config.add_route("detail", "/journal/{id:\d+}")
config.add_route("create", "/journal/new-entry")
config.add_route("update", "/journal/{id:\d+}/edit-entry")
config.add_route("login", "/login")
config.add_route("logout", "/logout")
1 change: 1 addition & 0 deletions learning_journal/scripts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# package
58 changes: 58 additions & 0 deletions learning_journal/scripts/initializedb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import os
import sys
import transaction
from datetime import datetime

from pyramid.paster import (
get_appsettings,
setup_logging,
)

from pyramid.scripts.common import parse_vars

from ..models.meta import Base
from ..models import (
get_engine,
get_session_factory,
get_tm_session,
)
from ..models import Entries


def usage(argv):
cmd = os.path.basename(argv[0])
print('usage: %s <config_uri> [var=value]\n'
'(example: "%s development.ini")' % (cmd, cmd))
sys.exit(1)


def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
config_uri = argv[1]
options = parse_vars(argv[2:])
setup_logging(config_uri)
settings = get_appsettings(config_uri, options=options)
settings["sqlalchemy.url"] = os.environ["DATABASE_URL"]

engine = get_engine(settings)
Base.metadata.create_all(engine)

session_factory = get_session_factory(engine)

ENTRIES = [
{"title": "There's value in stepping back.", "creation_date": datetime.strptime("December 14, 2016", "%B %d, %Y"), "body": """Sometimes principles from our past help with our present. Before starting the double linked list today, I committed to two things: Starting with the tests. Designing before coding. I actually wrote some tests first, just sort of stream of consciousness but things quickly got messy. After flailing for a bit, I stepped back and mapped out the function of each method and made a list of everything I would want test about it. From that, I was able to write a fairly comprehensive testing suite that should require little refactoring to really test all functions of the Double Linked List. Joey and I had some frustrating issues to work out with the server assignment. For whatever reason, since yesterday, our message sending on both the client and server sides decided to start escaping all the \r and \n characters. It had us and the TA's stumped for... far too long. We ended up changing our search parameters in our receiving while loops so that things could work again. Now I'm worried that I'll wake up tomorrow and it won't work. Or it just won't work whomever grades our assignment. SUCH IS LIFE."""},
{"title": "Revenge is Best SERVERED Cold", "creation_date": datetime.strptime("December 15, 2016", "%B %d, %Y"), "body": """The second you feel like youre starting to climb youre way out of the avalanche of work and information theres always some weird and random server error to pull you right back in. Im struggling a lot to find ways to refactor and clean up the server code. I feel like were working with a patchwork of try/except statements, teetering towers of if/else logic, and precarious while loops. Id like to see an example of a server done RIGHT. But I want to earn that - I want to finish my own server the hard way. Hey, urllib2, sup?"""},
{"title": "Whiteboarding", "creation_date": datetime.strptime("December 16, 2016", "%B %d, %Y"), "body": "Its fun, its hard, it makes your brain bend in ways you never thought possible. Really lucky I was partnered with Joey. He had the essential, Aha! moment that led to the solution. Glad I was able to contribute though. I worry that in the future as I prep for interviews, I'll won't be able to rely on flexible thinking to solve new problems. I'll likely have to plan ahead and try to expose myself to as many problem scenarios as possible in order to succeed. So it goes, more studying ahead. The server assignment really started as a hacky mess. Lots of patchwork try/except blocks and grabbing specific errors in desperation. I think today we really nailed it down into something I can be somewhat proud of. Looking forward to the opportunity to go back in time and refactor things this weekend. Also looking forward to beer."},
{"title": "Recent Awesome Things We've Learned", "creation_date": datetime.strptime("December 19, 2016", "%B %d, %Y"), "body": "<ul><li>Big(O) Notation</li><li>Linked-List based Data Structures</li><li>Web App Deployment Frameworks (Pyramid)</li></ul><p>It feels like we're moving beyond the introductory part of Python knowledge and are really starting to get into the meat n' potatoes of development and CS concepts. For the first time in a while, I'm starting to feel somewhat empowered as a dev-in-development. Not to say I know everything there is, but I know enough to now to actually make something with it, and more importantly, I'm starting to have some idea of what I don't know.</p><p>Despite the time crunch and the fast pace, I've enjoyed the class to this point. BUT, I am really excited for what is to come.</p><p>Of course, the second I begin to think this, the rug will be pulled out from underneath once again.</p><p>Looking forward to building stuff.</p>"},
{"title": "Tunnels, Lights, Progress, etc.", "creation_date": datetime.strptime("December 20, 2016", "%B %d, %Y"), "body": """Two quick things, the binary heap was initially a pain in the ass. However, I feel really proud of the implementation I have. Hooray for max OR min. Finally, I'm really excited for the group project. I feel a strong personal connection to the purpose of this app and I'm excited to put our newfound skills and knowledge to good use."""},
{"title": "They Said It Would Happen", "creation_date": datetime.strptime("December 21, 2016", "%B %d, %Y"), "body": "Unfortunately, I think I'm enjoying too much tinkering around with data structures. We have fallen far behind on the journal stuff. If we didn't have next week off, I'm not sure how it would all get done. Getting tough to balance home and school life. But that's the fun part, right? The challenge! BRING IT ON, COWBOY UP."},
{"title": "First Time Entering On the New Site", "creation_date": datetime.strptime("December 22, 2016", "%B %d, %Y"), "body": "This is pretty exciting. This is the first time I'll actually be writing my journal entry in the form I created! Of course it won't persist until I upgrade it to Postgres, BUT STILL. Pretty cool. Getting the chance to work from an emptier plate today really helped alleviate some pressure. I'm very much looking forward to next week for the opportunity to go back through past assignments with a fine tooth comb and improve things, first by learning from earlier mistakes, and second by getting some points back. Hopefully, time permitting, I'll be able to work a bit on the twitter app. Going back to Javascript after three intensive weeks of Python should be a trip."}
]

# with transaction.manager:
# dbsession = get_tm_session(session_factory, transaction.manager)

# for entry in ENTRIES:
# row = Entries(title=entry["title"], creation_date=entry["creation_date"], body=entry["body"])
# dbsession.add(row)
43 changes: 43 additions & 0 deletions learning_journal/security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import os
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.security import Allow, Everyone, Authenticated
from pyramid.session import SignedCookieSessionFactory
from passlib.apps import custom_app_context as pwd_context


class NewRoot(object):
def __init__(self, request):
self.request = request

__acl__ = [
(Allow, Authenticated, 'author'),
(Allow, Everyone, 'guest')
]


def check_credentials(username, password):
"""Return True if correct username and password, else False."""
if username and password:
# proceed to check credentials
if username == os.environ["AUTH_USERNAME"]:
return pwd_context.verify(password, os.environ["AUTH_PASSWORD"])
return False


def includeme(config):
"""Pyramid security configuration."""
auth_secret = os.environ.get("AUTH_SECRET", "potato")
authn_policy = AuthTktAuthenticationPolicy(
secret=auth_secret,
hashalg="sha512"
)
authz_policy = ACLAuthorizationPolicy()
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.set_root_factory(NewRoot)
# Begin CSRF security configurations.
session_secret = os.environ.get('SESSION_SECRET', 'superdupersecret')
session_factory = SignedCookieSessionFactory(session_secret)
config.set_session_factory(session_factory)
config.set_default_csrf_options(require_csrf=True)
19 changes: 19 additions & 0 deletions learning_journal/static/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
a {
text-decoration: none;
}

h1, h2, h3, h4, h5, h6{
font-family: Share Tech Mono;
}

h1{
font-size: 2.0em;
}

h2{
font-size: 1.5em;
}

h3{
font-size:1.25em;
}
18 changes: 18 additions & 0 deletions learning_journal/static/layout.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
header {
width: 100%;
height: 80px;
background-color: black;
}

header a{
font-family: Share Tech Mono;
color: white;
line-height: 40px;
padding-left: 8px;
}

main {
width: 90%;
height: 100%;
margin: auto;
}
Loading