from flask import Flask, render_template, request, redirect, jsonify, g
from flask import url_for, flash, make_response
from flask import session as login_session
from sqlalchemy import create_engine, asc
from sqlalchemy.orm import sessionmaker
from models import Base, Category, Item, User
from oauth2client.client import flow_from_clientsecrets
from oauth2client.client import FlowExchangeError
import httplib2
import json
import requests

app = Flask(__name__)

# Retrieves client ID's and secrets from the json files
CLIENT_ID = json.loads(open('client_secrets.json', 'r')
                       .read())['web']['client_id']
APP_ID = json.loads(open('fb_client_secrets.json', 'r')
                    .read())['web']['app_id']
APP_SECRET = json.loads(open('fb_client_secrets.json', 'r')
                        .read())['web']['app_secret']

# Connect to Database and create database session
engine = create_engine('sqlite:///catalog.db')
Base.metadata.bind = engine

DBSession = sessionmaker(bind=engine)
session = DBSession()


# Login handler
@app.route('/login')
def showLogin():
    """JSON API to view entire catalog Information."""
    return render_template('login.html')


# Third Party Oauth callback
@app.route('/oauth/<provider>', methods=['POST'])
def oauthLogin(provider):
    """
    Retrieves provider to process oauth login.
    params:(string) oauth provider
    """
    if provider == 'google':
        code = request.data
        try:
            # Upgrade auth code into credentials object
            oauth_flow = flow_from_clientsecrets('client_secrets.json',
                                                 scope='')
            oauth_flow.redirect_uri = 'postmessage'
            credentials = oauth_flow.step2_exchange(code)
        except FlowExchangeError:
            response = make_response(
                json.dumps('Failed to upgrade the authorization code.'), 401)
            response.headers['Content-Type'] = 'application/json'
            return response
        # Check for valid access token
        access_token = credentials.access_token
        url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?' \
              'access_token={}'.format(access_token)
        h = httplib2.Http()
        result = json.loads(h.request(url, 'GET')[1])

        # Access token error handling
        if result.get('error') is not None:
            response = make_response(json.dumps(result.get('error')), 500)
            response.headers['Content-Type'] = ' application/json'
            return response

        # Store access token in session
        login_session['provider'] = 'google'
        login_session['access_token'] = access_token
        login_session['gplus_id'] = credentials.id_token['sub']

        # Get user info
        userinfo_url = 'https://www.googleapis.com/oauth2/v1/userinfo'
        params = {'access_token': login_session['access_token'], 'alt': 'json'}
        answer = requests.get(userinfo_url, params=params)
        data = json.loads(answer.text)

        login_session['username'] = data['name']
        login_session['picture'] = data['picture']
        login_session['email'] = data['email']

    elif provider == 'facebook':
        access_token = request.data
        url = 'https://graph.facebook.com/oauth/access_token?grant_type=' \
        'fb_exchange_token&client_id={}&client_secret={}&' \
        'fb_exchange_token={}'.format(APP_ID, APP_SECRET, access_token)  # noqa
        h = httplib2.Http()
        result = json.loads(h.request(url, 'GET')[1])

        # Strip expire tag from access token
        access_token = result['access_token']

        url = 'https://graph.facebook.com/v2.11/me?access_token={}&fields=' \
        'name,id,email,picture'.format(access_token)  # noqa
        h = httplib2.Http()
        result = json.loads(h.request(url, 'GET')[1])

        # Get user info
        data = result

        login_session['access_token'] = access_token
        login_session['provider'] = 'facebook'
        login_session['username'] = data['name']
        login_session['email'] = data['email']
        login_session['picture'] = data['picture']['data']['url']
        login_session['facebook_id'] = data['id']

    # Checks if user exists in DB
    if getUserID(login_session['email']) is not None:
        login_session['user_id'] = getUserID(login_session['email'])
    else:
        createUser(login_session)
        login_session['user_id'] = getUserID(login_session['email'])

    # Stores token in session
    user = session.query(User).filter_by(email=login_session['email']).first()
    token = user.generate_auth_token(600)
    login_session['token'] = token

    output = ''
    output += '<h1>Welcome, {}!</h1>'.format(login_session['username'])
    output += '<img src="{}" '.format(login_session['picture'])
    output += 'style = "width: 300px; height: 300px; border-radius: 150px;' \
              '-webkit-border-radius: 150px;-moz-border-radius: 150px;">'
    flash('Now logged in as {}'.format(login_session['username']))
    return output


def createUser(login_session):
    newUser = User(username=login_session['username'],
                   email=login_session['email'],
                   picture=login_session['picture'])
    session.add(newUser)
    session.commit()


def getUserID(email):
    try:
        user = session.query(User).filter_by(email=email).one()
        return user.id
    except:
        return None


# Revoke current user's token and reset login_session
@app.route('/logout')
def logout():
    if 'provider' in login_session:
        if login_session['provider'] == 'google':
            del login_session['gplus_id']
        if login_session['provider'] == 'facebook':
            del login_session['facebook_id']
        del login_session['access_token']
        del login_session['username']
        del login_session['picture']
        del login_session['email']
        del login_session['token']
        flash("You have been successfully logged out.")
        return redirect(url_for('showCatalog'))
    else:
        flash("No user has been logged in.")
        return redirect(url_for('showCatalog'))


# JSON APIs to view Category Information.
@app.route('/catalog/JSON')
def catalogJSON():
    categories = session.query(Category).all()
    items = session.query(Item).order_by(Item.category_id).limit(3)

    return jsonify(Categories=[c.serialize for c in categories],
                   Items=[i.serialize for i in items])


@app.route('/catalog/<category>/JSON')
def catalogCategoryJSON(category):
    itemCategory = session.query(Category).filter_by(name=category).first()
    items = session.query(Item).filter_by(category_id=itemCategory.id).all()

    return jsonify(Categories=[itemCategory.serialize],
                   Items=[i.serialize for i in items])


@app.route('/catalog/<category>/<item>/JSON')
def categoryItemJSON(category, item):
    itemCategory = session.query(Category).filter_by(name=category).first()
    item = session.query(Item).filter_by(name=item,
                                         category_id=itemCategory.id).first()

    return jsonify(Category=[itemCategory.serialize],
                   Item=[item.serialize])


# Show all Categories and the latest items
@app.route('/')
@app.route('/catalog')
def showCatalog():
    categories = session.query(Category).all()
    items = session.query(Item).order_by(Item.category_id).limit(3)
    if 'token' not in login_session:
        return render_template('publiccatalog.html',
                               categories=categories, items=items)
    else:
        return render_template('catalog.html',
                               categories=categories, items=items)


# Show Items in a category item
@app.route('/catalog/<category>/')
def showCatalogCategory(category):
    itemCategory = session.query(Category).filter_by(name=category).first()
    items = session.query(Item).filter_by(category_id=itemCategory.id).all()
    categories = session.query(Category).all()
    if 'token' not in login_session:
        return render_template('publiccategory.html',
                               items=items, category=itemCategory,
                               categories=categories)
    else:
        return render_template('category.html', items=items,
                               category=itemCategory, categories=categories)


# Show an item in a category
@app.route('/catalog/<category>/<item>/')
def showCategoryItem(category, item):
    category = session.query(Category).filter_by(name=category).first()
    item = session.query(Item).filter_by(name=item,
                                         category_id=category.id).first()
    categories = session.query(Category).all()
    if 'token' not in login_session:
        return render_template('publiccategoryitem.html',
                               item=item, category=category,
                               categories=categories)
    return render_template('categoryitem.html', item=item,
                           category=category, categories=categories)


# Create a new item
@app.route('/catalog/category/new/', methods=['GET', 'POST'])
def newCategoryItem():
    if 'token' not in login_session:
        return redirect('/login')
    categories = session.query(Category).all()
    user = session.query(User).filter_by(email=login_session['email']).one()
    if request.method == 'POST':
        category = session.query(Category).filter_by(
            name=request.form['category']).first()
        newItem = Item(name=request.form['name'],
                       description=request.form['description'],
                       category_id=category.id, user_id=user.id)
        session.add(newItem)
        session.commit()
        flash('New Item {} Successfully Added'.format(newItem.name))
        return redirect(url_for('showCatalog'))
    else:
        return render_template('newcategoryitem.html', categories=categories)


# Edit a category item
@app.route('/catalog/<category>/<item>/edit', methods=['GET', 'POST'])
def editCategoryItem(category, item):
    if 'token' not in login_session:
        return redirect('/login')
    user = session.query(User).filter_by(email=login_session['email']).first()
    categoryItem = session.query(Category).filter_by(name=category).first()
    editedItem = session.query(Item).filter_by(
        name=item, category_id=categoryItem.id).first()
    categories = session.query(Category).all()
    if user.id != editedItem.user_id:
        flash('You are not authorized to edit {}.'.format(item))
        return redirect(url_for('showCategoryItem', category=categoryItem.name,
                                item=editedItem.name))
    if request.method == 'POST':
        if request.form['name']:
            editedItem.name = request.form['name']
        if request.form['description']:
            editedItem.description = request.form['description']
        if request.form['category']:
            category = session.query(Category).filter_by(
                name=request.form['category']).first()
            editedItem.category_id = category.id
        session.add(editedItem)
        session.commit()
        flash('Item Successfully Edited')
        return redirect(url_for('showCategoryItem',
                                category=request.form['category'],
                                item=editedItem.name))
    else:
        return render_template('editcategoryitem.html',
                               category=categoryItem.name,
                               item=editedItem.name, categories=categories,
                               editedItem=editedItem)


# Delete a category item
@app.route('/catalog/<category>/<item>/delete', methods=['GET', 'POST'])
def deleteCategoryItem(category, item):
    if 'token' not in login_session:
        return redirect('/login')
    user = session.query(User).filter_by(email=login_session['email']).first()
    categoryItem = session.query(Category).filter_by(name=category).first()
    itemToDelete = session.query(Item).filter_by(
        name=item, category_id=categoryItem.id).first()
    if user.id != itemToDelete.user_id:
        flash('You are not authorized to delete {}.'.format(item))
        return redirect(url_for('showCategoryItem', category=categoryItem.name,
                                item=itemToDelete.name))
    if request.method == 'POST':
        session.delete(itemToDelete)
        session.commit()
        flash('Item Successfully Deleted')
        return redirect(url_for('showCatalog'))
    else:
        return render_template('deletecategoryitem.html',
                               category=categoryItem.name,
                               item=itemToDelete.name)


if __name__ == '__main__':
    app.secret_key = 'N10kuN!'
    app.debug = True
    app.run(host='0.0.0.0', port=5000)

[((13, 6, 13, 21), 'flask.Flask', 'Flask', ({(13, 12, 13, 20): '__name__'}, {}), '(__name__)', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((24, 9, 24, 46), 'sqlalchemy.create_engine', 'create_engine', ({(24, 23, 24, 45): '"""sqlite:///catalog.db"""'}, {}), "('sqlite:///catalog.db')", False, 'from sqlalchemy import create_engine, asc\n'), ((27, 12, 27, 37), 'sqlalchemy.orm.sessionmaker', 'sessionmaker', (), '', False, 'from sqlalchemy.orm import sessionmaker\n'), ((35, 11, 35, 40), 'flask.render_template', 'render_template', ({(35, 27, 35, 39): '"""login.html"""'}, {}), "('login.html')", False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((134, 14, 136, 52), 'models.User', 'User', (), '', False, 'from models import Base, Category, Item, User\n'), ((175, 11, 176, 54), 'flask.jsonify', 'jsonify', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((184, 11, 185, 54), 'flask.jsonify', 'jsonify', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((194, 11, 195, 41), 'flask.jsonify', 'jsonify', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((238, 11, 239, 68), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((62, 12, 62, 27), 'httplib2.Http', 'httplib2.Http', ({}, {}), '()', False, 'import httplib2\n'), ((79, 17, 79, 58), 'requests.get', 'requests.get', (), '', False, 'import requests\n'), ((80, 15, 80, 38), 'json.loads', 'json.loads', ({(80, 26, 80, 37): 'answer.text'}, {}), '(answer.text)', False, 'import json\n'), ((162, 8, 162, 55), 'flask.flash', 'flash', ({(162, 14, 162, 54): '"""You have been successfully logged out."""'}, {}), "('You have been successfully logged out.')", False, 'from flask import url_for, flash, make_response\n'), ((165, 8, 165, 44), 'flask.flash', 'flash', ({(165, 14, 165, 43): '"""No user has been logged in."""'}, {}), "('No user has been logged in.')", False, 'from flask import url_for, flash, make_response\n'), ((205, 15, 206, 66), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((208, 15, 209, 66), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((219, 15, 221, 53), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((223, 15, 224, 76), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((235, 15, 237, 53), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((246, 15, 246, 33), 'flask.redirect', 'redirect', ({(246, 24, 246, 32): '"""/login"""'}, {}), "('/login')", False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((252, 18, 254, 64), 'models.Item', 'Item', (), '', False, 'from models import Base, Category, Item, User\n'), ((260, 15, 260, 77), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((267, 15, 267, 33), 'flask.redirect', 'redirect', ({(267, 24, 267, 32): '"""/login"""'}, {}), "('/login')", False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((288, 8, 288, 41), 'flask.flash', 'flash', ({(288, 14, 288, 40): '"""Item Successfully Edited"""'}, {}), "('Item Successfully Edited')", False, 'from flask import url_for, flash, make_response\n'), ((293, 15, 296, 53), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((303, 15, 303, 33), 'flask.redirect', 'redirect', ({(303, 24, 303, 32): '"""/login"""'}, {}), "('/login')", False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((315, 8, 315, 42), 'flask.flash', 'flash', ({(315, 14, 315, 41): '"""Item Successfully Deleted"""'}, {}), "('Item Successfully Deleted')", False, 'from flask import url_for, flash, make_response\n'), ((318, 15, 320, 54), 'flask.render_template', 'render_template', (), '', False, 'from flask import Flask, render_template, request, redirect, jsonify, g\n'), ((49, 25, 50, 58), 'oauth2client.client.flow_from_clientsecrets', 'flow_from_clientsecrets', (), '', False, 'from oauth2client.client import flow_from_clientsecrets\n'), ((91, 12, 91, 27), 'httplib2.Http', 'httplib2.Http', ({}, {}), '()', False, 'import httplib2\n'), ((99, 12, 99, 27), 'httplib2.Http', 'httplib2.Http', ({}, {}), '()', False, 'import httplib2\n'), ((163, 24, 163, 46), 'flask.url_for', 'url_for', ({(163, 32, 163, 45): '"""showCatalog"""'}, {}), "('showCatalog')", False, 'from flask import url_for, flash, make_response\n'), ((166, 24, 166, 46), 'flask.url_for', 'url_for', ({(166, 32, 166, 45): '"""showCatalog"""'}, {}), "('showCatalog')", False, 'from flask import url_for, flash, make_response\n'), ((258, 24, 258, 46), 'flask.url_for', 'url_for', ({(258, 32, 258, 45): '"""showCatalog"""'}, {}), "('showCatalog')", False, 'from flask import url_for, flash, make_response\n'), ((275, 24, 276, 53), 'flask.url_for', 'url_for', (), '', False, 'from flask import url_for, flash, make_response\n'), ((289, 24, 291, 53), 'flask.url_for', 'url_for', (), '', False, 'from flask import url_for, flash, make_response\n'), ((310, 24, 311, 55), 'flask.url_for', 'url_for', (), '', False, 'from flask import url_for, flash, make_response\n'), ((316, 24, 316, 46), 'flask.url_for', 'url_for', ({(316, 32, 316, 45): '"""showCatalog"""'}, {}), "('showCatalog')", False, 'from flask import url_for, flash, make_response\n'), ((55, 16, 55, 71), 'json.dumps', 'json.dumps', ({(55, 27, 55, 70): '"""Failed to upgrade the authorization code."""'}, {}), "('Failed to upgrade the authorization code.')", False, 'import json\n')]