Skip to content
Snippets Groups Projects
Commit 00b11aa0 authored by Nathan Bills's avatar Nathan Bills
Browse files

Merge pull request #7 from ndbills/develop

Pulling develop into master
parents 72827c83 07b5c481
Branches
Tags
No related merge requests found
Showing
with 595 additions and 78 deletions
......@@ -29,3 +29,18 @@ The idea is to create a responsive front-end that consumes a RESTful web service
* Public lists (allow your whole neighborhood to see and request to borrow your movies)
* Movie Reviews for a movie
* Google Analytics
#Dependencies
* MongoDB
* https://github.com/wagnerrp/pytmdb3/
#Setup
- Clone this directory
- in the same parent folder you cloned this directory clone the following dependency https://github.com/wagnerrp/pytmdb3/
- run the command `pip install -r requirements.txt`
- update the `__init__.py` in the project folder to point to your mongodb server
- start your mongodb server if it isn't running already
- run the `python runserver.py` in the MyMovieLibrary directory
- the application should be running on localhost:8080
\ No newline at end of file
# -*- coding: utf-8 -*-
__version__ = '0.1'
from flask import Flask
from functools import wraps
from flask import Flask, session, redirect, url_for, request
from flask_debugtoolbar import DebugToolbarExtension
from flask.ext.mongoengine import MongoEngine
#from mongoengine import *
#connect('my_movie_library')
import sys
sys.path.append('../pytmdb3/')
app = Flask('project')
app.config['SECRET_KEY'] = 'random'
app.config['MONGODB_SETTINGS'] = {'DB': 'my_movie_library'}
db = MongoEngine(app)
# app.config["MONGODB_SETTINGS"] = {'DB': "my_movie_library",
# 'host': '192.168.1.89'}
app.debug = True
toolbar = DebugToolbarExtension(app)
# toolbar = DebugToolbarExtension(app)
db = MongoEngine(app)
# app.debug = True
# toolbar = DebugToolbarExtension(app)
def security(role=None):
def wrapper(func):
@wraps(func)
def security_check(*args,**kwargs):
import json
user = None
if 'user' in session:
from project.model.User import User
user = json.loads(session['user'])
user = User.objects(email=user['email']).first()
kwargs['user'] = user
if role == None:
return func(*args,**kwargs);
if isinstance(role, list):
if len(role) == 0:
return func(*args,**kwargs);
elif user == None:
return redirect(url_for('login', next=request.url))
for r in role:
if r in user.roles:
return func(*args,**kwargs);
return redirect(url_for('login', next=request.url))
if user == None or role not in user.roles:
return redirect(url_for('login', next=request.url))
return func(*args,**kwargs);
return security_check
return wrapper
from project.controllers import *
# -*- coding: utf-8 -*-
from project import app
from project import app, security
from flask import render_template, request
from flask.ext.wtf import Form, TextField, validators
......@@ -9,16 +9,31 @@ class CreateForm(Form):
@app.route('/')
def start():
return render_template('home/home.html')
@security()
def start(user=None):
return render_template('home/home.html',user=user)
@app.route('/print', methods=['GET', 'POST'])
def printer():
form = CreateForm(request.form)
if request.method == 'POST' and form.validate():
from app.models.Printer import Printer
printer = Printer()
printer.show_string(form.text.data)
return render_template('home/index.html')
return render_template('home/print.html', form=form)
return render_template('printer/index.html')
# return render_template('printer/print.html', form=form)
@app.route('/test', methods=['GET', 'POST'])
def model():
from project.models.User import User
from project.models.Library import Library
from project.models.Movie import Movie
User.objects().delete()
Library.objects().delete()
Movie.objects().delete()
user = User(email='test@test.com', password='password').save()
testCollection = Library( user=user, unit='Movie', name='Action').save()
movie = Movie(title="SpiderMan",summary="SpiderMan gets the girl and saves the day").save()
movie.addTag('action').save()
user.addRole('kangaroo').save()
testCollection.addUnit(movie).save()
m = testCollection.getUnit(0)
print m.title
return render_template('printer/index.html')
# -*- coding: utf-8 -*-
from project import app, security
from flask import render_template, request, session
from flask.ext.wtf import Form, TextField, validators
from project.model.Library import Library
from project.model.User import User
import json
@app.route('/libraries')
@security('user')
def libraries(user = None):
libraries = Library.objects(user=user,unit='Movie')
return render_template('library/master.html', libraries=libraries,user=user)
@app.route('/libraries/<name>')
@security('user')
def library(name,user=None):
from project.model.Movie import Movie
library = Library.objects(user=user,name=name,unit='Movie').first()
return render_template('library/library.html',library=library)
@app.route('/libraries/<name>/<int:index>')
@security('user')
def libraryItem(name, index,user=None):
return render_template('library/libraryItem.html')
# -*- coding: utf-8 -*-
from project import app, security
from flask import render_template, request
from flask.ext.wtf import Form, TextField, validators
@app.route('/loaned')
@security('user')
def loaned(user=None):
return render_template('loan/master.html', user=user)
\ No newline at end of file
# -*- coding: utf-8 -*-
from project import app
from flask import render_template, request, session, redirect, url_for
from flask.ext.wtf import Form, TextField, validators
from project.model.User import User
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login/master.html')
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
user = User.validateUser(email, password)
if user is not None:
session['user'] = user.toJSON()
return redirect(url_for('libraries'))
else:
error = "That email or password is not valid. Please check your credentials and try again."
return render_template('login/master.html', error=error)
return render_template('login/master.html')
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'GET':
return render_template('login/signup.html')
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
passwordConfirm = request.form['passwordConfirm']
if password == passwordConfirm:
#check unique user
if len(User.objects(email=email)) == 0:
from project.model.Library import Library
#if unique, create user
user = User.createUser(email, password)
user.addRole('user').save()
session['user'] = user.toJSON()
Library(user=user, unit='Movie', name='Master').save()
Library(user=user, unit='Movie', name='Borrowed').save()
return redirect(url_for('libraries'))
else:
error = "The email provided is already in use with another account."
return render_template('login/signup.html', error=error, email=email)
else:
error = "The passwords you entered did not match. Please try again."
return render_template('login/signup.html', error=error, email=email)
return render_template('login/signup.html')
@app.route('/logout', methods=['GET', 'POST'])
def logout():
if 'user' in session:
session.pop('user')
return redirect(url_for('start'))
# -*- coding: utf-8 -*-
from project import app
from flask import render_template, request
from flask.ext.wtf import Form, TextField, validators
@app.route('/movies')
def movies():
return render_template('movies/master.html')
@app.route('/movies/<movieId>')
def movie_item(movieId):
return render_template('movies/movie.html')
......@@ -22,9 +22,8 @@ def test():
@app.route('/test-user')
def test_user():
from project.models.User import User
test_user = User(created_at=datetime.datetime.now(), email='test@user.org', password='password', role='admin').save()
print "date: %s" % test_user.created_at
from project.model.User import User
test_user = User.createUser(email='ndbills@gmail.com', password='password')
#check if test_user exists
#if yes, return a success render_template
#else add the user and return some other template
......
from project import db
class Group(db.Document):
category = db.StringField(max_length=50, required=True)
tags = db.ListField(db.StringField(max_length=50))
def addTag(self,tag):
if tag not in self.tags:
self.tags.append(tag)
return self
def removeTag(self,tag):
if tag in self.tags:
self.tags.remove(tag)
return self
def __str__(self):
return "Category: %s - %s" % (self.category, self.tags)
def __repr__(self):
return "{%s}" % (self.category)
\ No newline at end of file
from project import db
import User, sys, copy, Group
class Library(db.Document):
user = db.ReferenceField(User.User)
unit = db.StringField(max_length=50) #what Document the Library's collection relates to
name = db.StringField(max_length=100, unique_with=['user','unit']) #name of the Library
lookup_attribute = db.StringField(default='id')
collection = db.ListField(db.StringField())
summary = db.StringField()
def addUnit(self,unit):
if self.unit == type(unit).__name__:
value = unit[self.lookup_attribute]
if value is not None and value not in self.collection:
self.collection.append("%s" % value)
else:
return self
else:
raise Exception("Cannot add %s to Library of %s" % (type(unit).__name__,self.unit))
return self
def removeUnit(self,unit):
if self.unit == type(unit).__name__:
value = unit[self.lookup_attribute]
if value is not None and value in self.collection:
self.collection.remove("%s" % value)
else:
return self
else:
raise Exception("Cannot remove %s from Library of %s" % (type(unit).__name__,self.unit))
return self
# @param index --represents the index in the Library collection of the object
def hydrateUnit(self, index):
if index < 0 or index > self.collection.count:
raise Exception("Invalid index for Library %s" % self.name)
attr = {}
attr[self.lookup_attribute] = self.collection[index]
model = getattr(sys.modules["project.model.%s" % self.unit], self.unit)
return model.objects(**attr).first()
def hydrateList(self):
hydratedCollection = []
print sys.modules.keys()
model = getattr(sys.modules["project.model.%s" % self.unit], self.unit)
for index, hash_value in enumerate(self.collection):
attr = {}
attr[self.lookup_attribute] = self.collection[index]
unit = model.objects(**attr).first()
hydratedCollection.append(unit)
return hydratedCollection
def searchCollection(self,keyword,exclude =[] ,heirarchy = []):
"""
Searches the collection of this library for matches to the keyword
keyword String: keyword to search for
exclude List: list of attributes to exclude from the search
heirarchy List: order that matches should be returned
(first matches returned are always matches of multiple categories)
Return List of unique matches ordered by heirarchy if provided else by attribute order of object
"""
import datetime
hydratedCollection = self.hydrateList()
model = getattr(sys.modules["project.model.%s"%self.unit], self.unit)
model_attributes = model.__dict__['_db_field_map'].keys()
model_attributes = self.diff(model_attributes,exclude)
if len(heirarchy) > 0:
model_attributes = self.ordered_union(heirarchy,model_attributes)
result = [[]]
for attr in model_attributes:
result.append([]);
for unit in hydratedCollection:
for index,attr in enumerate(model_attributes):
value = unit[attr]
if isinstance(value, datetime.datetime):
value = value.isoformat()
if isinstance(value,list) and keyword.lower() in (val.lower() for val in value):
result[index].append(unit)
elif isinstance(value,basestring) and keyword.lower() in value.lower():
result[index].append(unit)
groups = Group.Group.objects(tags=keyword)
for group in groups:
if self.similar(group.tags,value):
result[-1].append(unit)
break
finish_result = []
for index,r in enumerate(result[:-1]):
for val in r:
for s in result[index:]:
if val in s:
if val not in finish_result:
finish_result.append(val)
for r in result:
additions = self.diff(r,finish_result)
finish_result.extend(additions)
return finish_result
@staticmethod
def diff(a, b):
b = set(b)
return [aa for aa in a if aa not in b]
@staticmethod
def ordered_intersect(a, b):
b = set(b)
return [aa for aa in a if aa in b]
@staticmethod
def ordered_union(a, b):
a.extend(Library.diff(b,a))
return a
@staticmethod
def similar(a,b):
for val in a:
if val in b:
return True
return False
def __str__(self):
return "Library {%s-%s}" % (self.name, self.unit)
def __repr__(self):
return self.__str__()
from project import db
import datetime
class Movie(db.Document):
created = db.DateTimeField(default=datetime.datetime.now, required=True)
title = db.StringField(max_length=255, required=True)
summary = db.StringField(max_length=10000, required=True)
tags = db.ListField(db.StringField(max_length=50))
def addTag(self,tag):
if tag not in self.tags:
self.tags.append(tag)
return self
def removeTag(self,tag):
if tag in self.tags:
self.tags.remove(tag)
return self
def __str__(self):
return self.title
def __repr__(self):
return str(self.toJSON())
def toJSON(self):
import json
return json.dumps({'created': self.created.isoformat(), 'title': self.title, 'summary': self.summary, 'tags': str(self.tags), 'id':str(self.id)})
\ No newline at end of file
from project import db
import datetime, hashlib, random
class User(db.Document):
created = db.DateTimeField(default=datetime.datetime.now(), required=True)
email = db.StringField(max_length=255, required=True, unique=True)
salt = db.StringField(max_length=255, required=True)
password = db.StringField(max_length=255, required=True)
roles = db.ListField(db.StringField(max_length=50))
def hasRole(self,role):
return role in self.roles
def addRole(self,role):
if role not in self.roles:
self.roles.append(role)
return self
def removeRole(self,role):
if role in self.roles:
self.roles.remove(role)
return self
def changePassword(self,password):
self.password = hashlib.sha224(self.salt.join(password)).hexdigest()
self.save()
return self
def toJSON(self):
import json
return json.dumps({'created': self.created.isoformat(), 'email': self.email, 'roles': self.roles, 'id':str(self.id)})
@staticmethod
def createUser(email, password):
user = User(email=email)
user.salt = str(random.getrandbits(128))
user.password = hashlib.sha224(user.salt.join(password)).hexdigest()
user.save()
return user
@staticmethod
def validateUser(email, password):
user = User.objects(email=email).first()
if user == None:
return None
password = hashlib.sha224(user.salt.join(password)).hexdigest()
if user.password == password:
return user
else:
return None
def __str__(self):
return self.email
def __repr__(self):
return str(self.toJSON())
meta = {
'allow_inheritance': True,
'indexes': ['-created', 'email'],
'ordering': ['-created']
}
\ No newline at end of file
File moved
# -*- coding: utf-8 -*-
from flask import flash
class Home(object):
def show_string(self, text):
if text == '':
flash("You didn't enter any text to flash")
else:
flash(text + "!!!")
# -*- coding: utf-8 -*-
from flask import flash
class Test(object):
def show_string(self, text):
if text == '':
flash("You didn't enter any text to flash")
else:
flash(text + "!!!")
\ No newline at end of file
// Set up the namespace
window.mml = window.mml || {};
mml.Widgets = mml.Widgets || {};
mml.Widgets.Login = function (options) {
/// <summary>
/// 1: Login(options) - This function accepts a JavaScript object containing the options to create a new login widget for a single user.
/// </summary>
/// <param name="options" type="object">
/// 1: url - The url of the service to retrieve data from.&#10;
/// 2: selector - The jQuery selector for the container the data will be put in.&#10;
/// </param>
/// <returns type="mml.Widgets.Login" />
if (!(this instanceof mml.Widgets.Login)) {
return new mml.Widgets.Login(options);
}
this.init(options); //This will setup the new instance
};
mml.Widgets.Login.prototype = (function () {
var init;
init = function(options){
var template;
template = '';
$(options.selectedElement).append(template);
};
/* PUBLIC */
return {
init: init
};
}());
\ No newline at end of file
{% extends "index.html" %}
{% extends "usernav.html" if user else 'index.html'%}
{% block content %}
<div class="col-sm-12">
<div class="jumbotron">
<h1>It's working!!!</h1>
<p>Here, all of your wildest dreams can come true.</p>
......@@ -24,4 +25,5 @@
</div>
</div>
</div>
</div>
{% endblock %}
......@@ -35,15 +35,20 @@
<ul class="nav navbar-nav">
<li><a href="https://github.com/ndbills/MyMovieLibrary/" target="_blank">GitHub</a></li>
</ul>
{% block usernav %}
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Login</a></li>
<li><a href="#">Sign-up</a></li>
<li><a href="{{ url_for('login') }}">Login</a></li>
<li><a href="{{ url_for('signup') }}">Sign-up</a></li>
</ul>
{% endblock %}
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav><!-- end navbar -->
<div class="row">
{% block content %}{% endblock %}
</div>
<div class="panel panel-default">
<div class="panel-body">
......@@ -58,17 +63,8 @@
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename='lib/bootstrap.min.js') }}"></script>
<!--<h1>Flask MVC Boilerplate</h1>
{#{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% macro render_error(field) %}
{% if field.errors %}
{% for error in field.errors %}<span class='error'>{{ error }}</span>{% endfor %}
{% endif %}
{% endmacro %}
{% block body %}{% endblock %}#}-->
{% block javascript %}
{% endblock %}
</div>
</body>
</html>
{% extends "usernav.html" %}
{% block content %}
<div class="col-sm-12">
<div class="col-md-4">
<h3>{{ library.name ~ ' ' ~ library.unit }} Library </h3>
</div>
<div class="btn-group pull-right">
<button type="button" class="btn btn-default add-movie" data-id='{{ library.name }}'>Add Movie</button>
</div>
<table class="table table-striped table-hover">
<thead>
<tr>
<td>Movie</td>
<td>Summary</td>
<td>Rating</td>
<td>Options</td>
</tr>
</thead>
<tbody>
{% if library.collection|length <= 0 %}
<tr>
<td>No Movies are in this collection </td>
</tr>
{% else %}
{% for movie in library.hydrateList() %}
<tr>
<td>
<img src="{{ movie.image|default('') }}" style="max-width: 200px;" alt="{{ movie.title }}">
<p>{{ movie.title }}</p>
</td>
<td>
{{ movie.summary }}
</td>
<td>
{{ movie.rating }}
</td>
<td>
<div class="btn-group-vertical">
<button type="button" class="btn btn-default btn-small lend-movie" data-id='{{ movie.id }}'>Lend</button>
<button type="button" class="btn btn-default btn-small borrow-movie" data-id='{{ movie.id }}'>Edit</button>
<button type="button" class="btn btn-default btn-small remove-movie" data-id='{{ movie.id }}'>Remove</button>
</div>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
{% endblock %}
\ No newline at end of file
{% extends "usernav.html" %}
{% block content %}
<div class="col-sm-12">
<div class="row">
<div class="col-xs-12 col-sm-3 col-md-2">
<img data-src="holder.js/100%x200/sky" alt="movie title">
<button type="button" class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-pencil"></span> Edit
</button>
<button type="button" class="btn btn-warning btn-sm">
<span class="glyphicon glyphicon-remove"></span> Remove
</button>
</div>
<div class="col-xs-12 col-sm-3 col-md-2">
<ul class="list-unstyled">
<li>Movie Title</li>
<li>Director(s)</li>
<li>Actors (truncated)</li>
<li>Producers</li>
<li>Composer</li>
</ul>
</div>
<div class="col-xs-12 col-sm-6 col-md-8">
<h2>Summary</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eget metus vitae mi interdum tristique. Integer malesuada quam tincidunt, gravida nisi vehicula, auctor mi. Morbi fermentum, neque quis adipiscing placerat, odio nunc consequat ligula, vel sagittis nisl orci id risus. Aliquam hendrerit, elit a molestie suscipit, eros tortor scelerisque odio, sit amet cursus ligula lorem et turpis. Pellentesque ante metus, suscipit id blandit eget, vehicula quis tortor. Aliquam a pharetra enim. Morbi in purus sit amet nulla egestas consectetur. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla diam nulla, iaculis lobortis varius at, auctor malesuada enim. Donec a nisl eget ligula rhoncus fermentum.</p>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-12">
<h3>Movie Reviews</h3>
<p>Coming Soon!</p>
</div>
</div>
</div>
{% endblock %}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment