From a187ac33fa1892b1f633f9e19be0a4dd092e4765 Mon Sep 17 00:00:00 2001
From: "Sachin S. Kamath" <sskamath96@gmail.com>
Date: Fri, 20 Oct 2017 19:30:18 +0530
Subject: [PATCH] Enhance user model and add user creation endpoint

Signed-off-by: Sachin S. Kamath <sskamath96@gmail.com>
---
 hodor/__init__.py         | 11 +++----
 hodor/controllers/user.py | 62 ++++++++++++++++++++++++++++++++++++---
 hodor/models/user.py      | 26 +++++++++++-----
 hodor/tests/user.py       |  1 +
 instance/config.py        |  6 ++--
 manage.py                 |  3 +-
 requirements.txt          |  3 +-
 7 files changed, 90 insertions(+), 22 deletions(-)
 create mode 100644 hodor/tests/user.py

diff --git a/hodor/__init__.py b/hodor/__init__.py
index cdcc372..a4c0d45 100644
--- a/hodor/__init__.py
+++ b/hodor/__init__.py
@@ -1,11 +1,8 @@
 # -*- coding: utf-8 -*-
 # app/__init__.py
-import time
 from termcolor import colored
-import sys
 from flask_api import FlaskAPI
 from flask_sqlalchemy import SQLAlchemy
-# local import
 from instance.config import app_config
 
 # initialize sql-alchemy
@@ -21,16 +18,20 @@ application.config.from_object(app_config[config_name])
 application.config.from_pyfile('config.py')
 application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
 db.init_app(application)
+application.config['SECRET_KEY'] = 'blehblehbleh'
+
+
 
 # Delay the application launch to get the user attention if running in testing mode
+'''
 if config_name != 'production':
     for x in range(0, 5):
         sys.stdout.write(colored("\rYou are not running the server in production mode. " +
               "App will run in {} seconds..".format(5-x), 'red'))
         sys.stdout.flush()
         time.sleep(1)
-
-    print(colored("\n\nRunning app in {} mode..".format(config_name), 'yellow'))
+'''
+print(colored("\n\nRunning app in {} mode..".format(config_name), 'yellow'))
 
 # This disables the HTML API renderer for flask_api when called through browsers.
 if config_name == 'production':
diff --git a/hodor/controllers/user.py b/hodor/controllers/user.py
index e8ddfce..55655de 100644
--- a/hodor/controllers/user.py
+++ b/hodor/controllers/user.py
@@ -1,11 +1,65 @@
 # -*- coding: utf-8 -*-
 from hodor import app
-from flask import jsonify
+from flask import request, jsonify, abort, make_response
+from hodor.models.user import User
+from sqlalchemy.exc import IntegrityError
 
 
-@app.route('/')
-def start():
-    return jsonify({'hello': 'world'})
+# Get all Users
+@app.route('/users', methods=['GET', 'POST'])
+def get_all_users():
+    response = {}
+    response['status'] = 200
+    response ['data'] = []
+    """
+    This function iterates the database to find all users and returns it as JSON.
+    :return: Response Code
+    """
+
+    for ele in User.get_all():
+        user = ele.__dict__
+        print user
+    return 'Work in Progress'
+
+
+@app.route('/users/new', methods=['GET', 'POST'])
+def add_new_user():
+    """
+    This function adds a new user
+    :return: Response Code
+    """
+    newuser = {}
+
+    if request.method == "POST":
+        try:
+            newuser['username'] = str(request.data.get('username').strip())
+            newuser['first_name'] = str(request.data.get('first_name').strip())
+            newuser['last_name'] = str(request.data.get('last_name').strip())
+            newuser['email'] = str(request.data.get('email').strip())
+            newuser['password'] = str(request.data.get('password').strip())
+            newuser['verification_code'] = str(request.data.get('verification_code').strip()) or newuser['verification_code']
+        except Exception as e:
+            print(e)
+            abort(500)
+        print newuser
+        user = User(**newuser)
+        user.save()
+    return make_response(jsonify(status=201, msg="User {} successfully added to database".format(user.username)), 201)
+
+
+@app.errorhandler(IntegrityError)
+def handle_sqlalchemy_assertion_error(err):
+    try:
+        '''
+        err.orig.args is from the DBAPIError class of SQLAlchemy. It usually contains the original error message.
+        The below is an attempt to clean up the message and only return the relevant part to API
+        '''
+        errmsg = err.orig.args[0].split('\n')[1][9:]
+    except IndexError:
+        errmsg = err.orig.args[0].split('\n')
+    except IndexError:
+        errmsg = err.orig.args[0]
+    return make_response(jsonify(status=400, msg=errmsg), 400)
 
 
 @app.route('/print', methods=['GET', 'POST'])
diff --git a/hodor/models/user.py b/hodor/models/user.py
index 05ee19c..e637410 100644
--- a/hodor/models/user.py
+++ b/hodor/models/user.py
@@ -1,16 +1,26 @@
 # -*- coding: utf-8 -*-
 
 from hodor import db
+from sqlalchemy import inspect
+from sqlalchemy_utils import PasswordType
 
 class User(db.Model):
     __tablename__ = 'users'
 
     # Values entered by the user
-    username = db.Column(db.String(32), primary_key=True)
+    username = db.Column(db.String(32), primary_key=True, nullable=False)
     first_name = db.Column(db.String(32), nullable=False)
     last_name = db.Column(db.String(32), nullable=False)
     email = db.Column(db.String(64), unique=True, nullable=False)
-    password = db.Column(db.String(64), nullable=False)
+    '''PasswordType is an awesome function. To check for passwords later, 
+        you can just do user['password'] == 'plaintext' for a boolean response.'''
+    password = db.Column(PasswordType(
+        schemes=[
+            'pbkdf2_sha512',
+            'md5_crypt'
+        ],
+        deprecated=['md5_crypt']
+    ), nullable=False)
 
     # Platform values
     disabled = db.Column(db.Boolean, default=False)
@@ -20,11 +30,9 @@ class User(db.Model):
     def __unicode__(self):
         return unicode(self.username)
 
-    def __init__(self, name):
-        """initialize with name."""
-        self.name = name
-
     def save(self):
+        print type(self)
+        print self.email
         db.session.add(self)
         db.session.commit()
 
@@ -32,9 +40,13 @@ class User(db.Model):
     def get_all():
         return User.query.all()
 
+    def get_all_dict(self):
+        return {c.key: getattr(self, c.key)
+                for c in inspect(self).mapper.column_attrs}
+
     def delete(self):
         db.session.delete(self)
         db.session.commit()
 
     def __repr__(self):
-        return "<User: {}>".format(self.name)
\ No newline at end of file
+        return "<User: {}>".format(self.name)
diff --git a/hodor/tests/user.py b/hodor/tests/user.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/hodor/tests/user.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/instance/config.py b/instance/config.py
index 0cd5642..ae51691 100644
--- a/instance/config.py
+++ b/instance/config.py
@@ -13,10 +13,10 @@ class Config(object):
 
 class DevelopmentConfig(Config):
     """Configurations for Development."""
-    DEBUG = False
-    SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/test_db'
+    DEBUG = True
+    SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/hodor_test'
     SECRET = "ChangeThisStringIfYouWant"
-    TESTING = False
+    TESTING = True
 
 
 class TestingConfig(Config):
diff --git a/manage.py b/manage.py
index 73263fc..e0fc5bb 100644
--- a/manage.py
+++ b/manage.py
@@ -1,8 +1,7 @@
 from flask_script import Manager # class for handling a set of commands
 from flask_migrate import Migrate, MigrateCommand
-from hodor import db, create_app
+from hodor import db, app
 
-app = create_app(config_name='development')
 
 migrate = Migrate(app, db)
 manager = Manager(app)
diff --git a/requirements.txt b/requirements.txt
index a7e0385..bd50cd1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,4 +4,5 @@ Jinja2
 Flask-DebugToolbar
 flask_api
 termcolor
-flask_sqlalchemy
\ No newline at end of file
+flask_sqlalchemy
+sqlalchemy_utils
\ No newline at end of file
-- 
GitLab