<reponame>nissant/dgi-portfolio-tracker
from flask import render_template, redirect, url_for, session, make_response
from functools import wraps
from sqlalchemy.ext.declarative import DeclarativeMeta
from api.model import Accounts
import json

TIME_STAMP_UTC_FORMAT = '%Y-%m-%d %H:%M:%S.%f'


def login_required(f):
    """
    Decorate routes to require login.

    https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if session.get("user_id") is None:
            return redirect(url_for('auth.login'))
        return f(*args, **kwargs)
    return decorated_function


def account_required(f):
    """
    Decorate routes to require user account.

    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        user_account: Accounts = Accounts.query.filter_by(account_id=kwargs.get("account_id"),
                                                          user_id=session.get("user_id")).first()
        if user_account is None:
            return make_response(f"Could not find user account ID: {kwargs.get('account_id')}!", 500)
        return f(*args, **kwargs)
    return decorated_function


def transaction_based_account_required(f):
    """
    Decorate routes to require transaction based user account.
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        user_account: Accounts = Accounts.query.filter_by(account_id=kwargs.get("account_id"),
                                                          user_id=session.get("user_id")).first()
        if user_account is None:
            return make_response(f"Could not find user account ID: {kwargs.get('account_id')}!", 500)
        if user_account.is_institute_integrated:
            return make_response("Can't calculate positions on institute integrated account!", 500)
        elif not user_account.is_transaction_based_positions:
            return make_response("Account must be configured as transaction based!", 500)
        return f(*args, **kwargs)
    return decorated_function


def apology(message, code=400):
    """Render message as an apology to user."""
    def escape(s):
        """
        Escape special characters.

        https://github.com/jacebrowning/memegen#special-characters
        """
        for old, new in [("-", "--"), (" ", "-"), ("_", "__"), ("?", "~q"),
                         ("%", "~p"), ("#", "~h"), ("/", "~s"), ("\"", "''")]:
            s = s.replace(old, new)
        return s
    return render_template("apology.html", top=code, bottom=escape(message)), code


def usd(value):
    """Format value as USD."""
    return f"${value:,.2f}"


class EnhancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj.__class__, DeclarativeMeta):
            # an SQLAlchemy class
            fields = {}
            for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                data = obj.__getattribute__(field)
                try:
                    json.dumps(data)    # this will fail on non-encodable values, like other classes
                    fields[field] = data
                except TypeError:
                    fields[field] = None
            # a json-encoded dict
            return fields


def make_object_as_json_response(obj):
    return make_response(json.dumps(obj, cls=EnhancedJSONEncoder, indent=4), 200)

from flask import render_template, redirect, url_for, session, make_response
from functools import wraps
from sqlalchemy.ext.declarative import DeclarativeMeta
from api.model import Accounts
import json

TIME_STAMP_UTC_FORMAT = '%Y-%m-%d %H:%M:%S.%f'


def login_required(f):
    """
    Decorate routes to require login.

    https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if session.get("user_id") is None:
            return redirect(url_for('auth.login'))
        return f(*args, **kwargs)
    return decorated_function


def account_required(f):
    """
    Decorate routes to require user account.

    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        user_account: Accounts = Accounts.query.filter_by(account_id=kwargs.get("account_id"),
                                                          user_id=session.get("user_id")).first()
        if user_account is None:
            return make_response(f"Could not find user account ID: {kwargs.get('account_id')}!", 500)
        return f(*args, **kwargs)
    return decorated_function


def transaction_based_account_required(f):
    """
    Decorate routes to require transaction based user account.
    """
    @wraps(f)
    def decorated_function(*args, **kwargs):
        user_account: Accounts = Accounts.query.filter_by(account_id=kwargs.get("account_id"),
                                                          user_id=session.get("user_id")).first()
        if user_account is None:
            return make_response(f"Could not find user account ID: {kwargs.get('account_id')}!", 500)
        if user_account.is_institute_integrated:
            return make_response("Can't calculate positions on institute integrated account!", 500)
        elif not user_account.is_transaction_based_positions:
            return make_response("Account must be configured as transaction based!", 500)
        return f(*args, **kwargs)
    return decorated_function


def apology(message, code=400):
    """Render message as an apology to user."""
    def escape(s):
        """
        Escape special characters.

        https://github.com/jacebrowning/memegen#special-characters
        """
        for old, new in [("-", "--"), (" ", "-"), ("_", "__"), ("?", "~q"),
                         ("%", "~p"), ("#", "~h"), ("/", "~s"), ("\"", "''")]:
            s = s.replace(old, new)
        return s
    return render_template("apology.html", top=code, bottom=escape(message)), code


def usd(value):
    """Format value as USD."""
    return f"${value:,.2f}"


class EnhancedJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj.__class__, DeclarativeMeta):
            # an SQLAlchemy class
            fields = {}
            for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
                data = obj.__getattribute__(field)
                try:
                    json.dumps(data)    # this will fail on non-encodable values, like other classes
                    fields[field] = data
                except TypeError:
                    fields[field] = None
            # a json-encoded dict
            return fields


def make_object_as_json_response(obj):
    return make_response(json.dumps(obj, cls=EnhancedJSONEncoder, indent=4), 200)
Decorate routes to require login.      https://flask.palletsprojects.com/en/1.1.x/patterns/viewdecorators/ Decorate routes to require user account. Decorate routes to require transaction based user account. Render message as an apology to user. Escape special characters.          https://github.com/jacebrowning/memegen#special-characters Format value as USD. # an SQLAlchemy class # this will fail on non-encodable values, like other classes # a json-encoded dict