<commit_before>"""
An storage service for use by a Jenca Cloud authentication service.
"""

import os

from flask import Flask, json, jsonify, request, make_response

from flask.ext.sqlalchemy import SQLAlchemy
from flask_jsonschema import JsonSchema, ValidationError
from flask_negotiate import consumes

from requests import codes

db = SQLAlchemy()


class User(db.Model):
    """
    A user has an email and password.
    """
    email = db.Column(db.String, primary_key=True)
    password_hash = db.Column(db.String)


def create_app(database_uri):
    """
    Create an application with a database in a given location.

    :param database_uri: The location of the database for the application.
    :type database_uri: string
    :return: An application instance.
    :rtype: ``Flask``
    """
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = database_uri
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
    db.init_app(app)

    with app.app_context():
        db.create_all()

    return app

SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI',
                                         'sqlite:///:memory:')
app = create_app(database_uri=SQLALCHEMY_DATABASE_URI)

# Inputs can be validated using JSON schema.
# Schemas are in app.config['JSONSCHEMA_DIR'].
# See https://github.com/mattupstate/flask-jsonschema for details.
app.config['JSONSCHEMA_DIR'] = os.path.join(app.root_path, 'schemas')
jsonschema = JsonSchema(app)


def load_user_from_id(user_id):
    """
    :param user_id: The ID of the user Flask is trying to load.
    :type user_id: string
    :return: The user which has the email address ``user_id`` or ``None`` if
        there is no such user.
    :rtype: ``User`` or ``None``.
    """
    return User.query.filter_by(email=user_id).first()


@app.errorhandler(ValidationError)
def on_validation_error(error):
    """
    :resjson string title: An explanation that there was a validation error.
    :resjson string message: The precise validation error.
    :status 400:
    """
    return jsonify(
        title='There was an error validating the given arguments.',
        # By default on Python 2 errors will look like:
        # "u'password' is a required property".
        # This removes all "u'"s, and so could be dangerous.
        detail=error.message.replace("u'", "'"),
    ), codes.BAD_REQUEST


@app.route('/users/<email>', methods=['GET'])
@consumes('application/json')
def get_user(email):
    """
    Get information about particular user.

    :reqheader Content-Type: application/json
    :resheader Content-Type: application/json
    :resjson string email: The email address of the new user.
    :resjson string password_hash: <PASSWORD>.
    :status 200: The requested user's information is returned.
    :status 404: There is no user with the given ``email``.
    """
    user = load_user_from_id(email)

    if user is None:
        return jsonify(
            title='The requested user does not exist.',
            detail='No user exists with the email "{email}"'.format(
                email=email),
        ), codes.NOT_FOUND

    return_data = jsonify(email=user.email, password_hash=user.password_hash)
    return return_data, codes.OK


@app.route('/users', methods=['GET', 'POST'])
@consumes('application/json')
# TODO validate schema only for POST
# @jsonschema.validate('users', 'create')
def users_route():
    """
    # TODO figure out separating docs for get and post

    Create a new user.

    :param email: The email address of the new user.
    :type email: string
    :param password_hash: A password hash to associate with the given ``email``
        address.
    :type password_hash: string
    :reqheader Content-Type: application/json
    :resheader Content-Type: application/json
    :resjson string email: The email address of the new user.
    :resjson string password_hash: The password hash of the new user.
    :status 200: A user with the given ``email`` and ``password_hash`` has been
        created.
    :status 409: There already exists a user with the given ``email``.
    """
    if request.method == 'POST':
        email = request.json['email']
        password_hash = request.json['password_hash']

        if load_user_from_id(email) is not None:
            return jsonify(
                title='There is already a user with the given email address.',
                detail='A user already exists with the email "{email}"'.format(
                    email=email),
            ), codes.CONFLICT

        user = User(email=email, password_hash=password_hash)
        db.session.add(user)
        db.session.commit()

        return jsonify(email=email, password_hash=password_hash), codes.CREATED
    elif request.method == 'GET':
        details = [
            {'email': user.email, 'password_hash': user.password_hash} for user
            in User.query.all()]

        return make_response(
            json.dumps(details),
            codes.OK,
            {'Content-Type': 'application/json'})

if __name__ == '__main__':   # pragma: no cover
    # Specifying 0.0.0.0 as the host tells the operating system to listen on
    # all public IPs. This makes the server visible externally.
    # See http://flask.pocoo.org/docs/0.10/quickstart/#a-minimal-application
    app.run(host='0.0.0.0', port=5001, debug=True)<commit_msg>All tests pass now - separate function for creating a user<commit_after>"""
An storage service for use by a Jenca Cloud authentication service.
"""

import os

from flask import Flask, json, jsonify, request, make_response

from flask.ext.sqlalchemy import SQLAlchemy
from flask_jsonschema import JsonSchema, ValidationError
from flask_negotiate import consumes

from requests import codes

db = SQLAlchemy()


class User(db.Model):
    """
    A user has an email and password.
    """
    email = db.Column(db.String, primary_key=True)
    password_hash = db.Column(db.String)


def create_app(database_uri):
    """
    Create an application with a database in a given location.

    :param database_uri: The location of the database for the application.
    :type database_uri: string
    :return: An application instance.
    :rtype: ``Flask``
    """
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = database_uri
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
    db.init_app(app)

    with app.app_context():
        db.create_all()

    return app

SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI',
                                         'sqlite:///:memory:')
app = create_app(database_uri=SQLALCHEMY_DATABASE_URI)

# Inputs can be validated using JSON schema.
# Schemas are in app.config['JSONSCHEMA_DIR'].
# See https://github.com/mattupstate/flask-jsonschema for details.
app.config['JSONSCHEMA_DIR'] = os.path.join(app.root_path, 'schemas')
jsonschema = JsonSchema(app)


def load_user_from_id(user_id):
    """
    :param user_id: The ID of the user Flask is trying to load.
    :type user_id: string
    :return: The user which has the email address ``user_id`` or ``None`` if
        there is no such user.
    :rtype: ``User`` or ``None``.
    """
    return User.query.filter_by(email=user_id).first()


@app.errorhandler(ValidationError)
def on_validation_error(error):
    """
    :resjson string title: An explanation that there was a validation error.
    :resjson string message: The precise validation error.
    :status 400:
    """
    return jsonify(
        title='There was an error validating the given arguments.',
        # By default on Python 2 errors will look like:
        # "u'password' is a required property".
        # This removes all "u'"s, and so could be dangerous.
        detail=error.message.replace("u'", "'"),
    ), codes.BAD_REQUEST


@app.route('/users/<email>', methods=['GET'])
@consumes('application/json')
def get_user(email):
    """
    Get information about particular user.

    :reqheader Content-Type: application/json
    :resheader Content-Type: application/json
    :resjson string email: The email address of the new user.
    :resjson string password_hash: The password of the new user.
    :status 200: The requested user's information is returned.
    :status 404: There is no user with the given ``email``.
    """
    user = load_user_from_id(email)

    if user is None:
        return jsonify(
            title='The requested user does not exist.',
            detail='No user exists with the email "{email}"'.format(
                email=email),
        ), codes.NOT_FOUND

    return_data = jsonify(email=user.email, password_hash=user.password_hash)
    return return_data, codes.OK


@jsonschema.validate('users', 'create')
def create_user():
    """
    Create a new user. See ``users_route`` for details.
    """
    email = request.json['email']
    password_hash = request.json['password_hash']

    if load_user_from_id(email) is not None:
        return jsonify(
            title='There is already a user with the given email address.',
            detail='A user already exists with the email "{email}"'.format(
                email=email),
        ), codes.CONFLICT

    user = User(email=email, password_hash=password_hash)
    db.session.add(user)
    db.session.commit()

    return jsonify(email=email, password_hash=password_hash), codes.CREATED


@app.route('/users', methods=['GET', 'POST'])
@consumes('application/json')
def users_route():
    """
    # TODO figure out separating docs for get and post

    Create a new user.

    :param email: The email address of the new user.
    :type email: string
    :param password_hash: A password hash to associate with the given ``email``
        address.
    :type password_hash: string
    :reqheader Content-Type: application/json
    :resheader Content-Type: application/json
    :resjson string email: The email address of the new user.
    :resjson string password_hash: The password hash of the new user.
    :status 200: A user with the given ``email`` and ``password_hash`` has been
        created.
    :status 409: There already exists a user with the given ``email``.
    """
    if request.method == 'POST':
        return create_user()
    elif request.method == 'GET':
        details = [
            {'email': user.email, 'password_hash': user.password_hash} for user
            in User.query.all()]

        return make_response(
            json.dumps(details),
            codes.OK,
            {'Content-Type': 'application/json'})

if __name__ == '__main__':   # pragma: no cover
    # Specifying 0.0.0.0 as the host tells the operating system to listen on
    # all public IPs. This makes the server visible externally.
    # See http://flask.pocoo.org/docs/0.10/quickstart/#a-minimal-application
    app.run(host='0.0.0.0', port=5001, debug=True)
{'fraction_non_alphanumeric': 0.1078684891023273, 'fraction_numerical': 0.0057258958256372365, 'mean_word_length': 3.042179917879806, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 6, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1633916', 'n_tokens_mistral': 3263, 'n_tokens_neox': 3153, 'n_words': 1009}