# Path: armet/exceptions.py
# class ImproperlyConfigured(BaseException):
#     """Something has been set up or configured incorrectly.
#     """
# 
# Path: armet/query/parser.py
# COMBINATORS = {
#     constants.LOGICAL_AND: operator.and_,
#     constants.LOGICAL_OR: operator.or_}
# OPERATOR_BEGIN_CHARS = set(x[0] for _, x in constants.OPERATORS if x)
# OPERATOR_SYMBOL_MAP = dict((v, k) for k, v in constants.OPERATORS if v)
# OPERATOR_KEYWORDS = set(k for k, _ in constants.OPERATORS)
# REVERSED_OPERATOR_SUFFIX_MAP = dict(
#     (v, k) for k, v in constants.OPERATOR_SUFFIX_MAP.items()
# )
# class Query(object):
# class QuerySegment(object):
# class NoopQuerySegment(object):
# class BinarySegmentCombinator(object):
# class UnarySegmentCombinator(object):
#     def __init__(self, original, parsed):
#     def __init__(self, **kwargs):
#     def __repr__(self):
#     def __str__(self):
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, left, right, operation=operator.and_):
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, operand, operation=operator.not_):
#     def __repr__(self):
#     def __str__(self):
# def parse(text, encoding='utf8'):
# def reset_stringio(buf):
# def split_segments(text, closing_paren=False):
# def combine(segments, combinators):
# def parse_directive(key):
# def parse_segment(text):
# 
# Path: armet/query/constants.py
# OPERATOR_EQUAL = 'exact', '=='
# OPERATOR_IEQUAL = 'iexact', '='
# OPERATOR_LT = 'lt', '<'
# OPERATOR_LTE = 'lte', '<='
# OPERATOR_GT = 'gt', '>'
# OPERATOR_GTE = 'gte', '>='
# OPERATOR_REGEX = 'regex', '*='
# OPERATOR_ISNULL = 'isnull', None
# OPERATOR_ICONTAINS = 'icontains', None
# OPERATOR_FALLBACK = OPERATOR_IEQUAL
# OPERATOR_SUFFIX_FALLBACK = OPERATOR_FALLBACK[0]
# OPERATOR_EQUALITY_FALLBACK = OPERATOR_FALLBACK[1]
# OPERATOR_MAP = {
#     OPERATOR_EQUAL: operator.eq,
#     OPERATOR_IEQUAL: lambda x, y: x.lower() == y.lower(),
#     OPERATOR_LT: operator.lt,
#     OPERATOR_LTE: operator.le,
#     OPERATOR_GT: operator.gt,
#     OPERATOR_GTE: operator.ge,
#     OPERATOR_REGEX: lambda x, y: re.search(y, x),
#     OPERATOR_ISNULL: lambda x: x is None,
#     OPERATOR_ICONTAINS: operator.contains,
# }
# OPERATORS = set(OPERATOR_MAP.keys())
# OPERATOR_SUFFIXES = set(filter(None, map(operator.itemgetter(0), OPERATORS)))
# OPERATOR_EQUALITIES = set(filter(None, map(operator.itemgetter(1), OPERATORS)))
# OPERATOR_SUFFIX_MAP = dict(
#     filter(operator.itemgetter(0),
#            map(lambda x: (x[0][0], x[1]),
#                OPERATOR_MAP.items())))
# OPERATOR_EQUALITY_MAP = dict(
#     filter(operator.itemgetter(0),
#            map(lambda x: (x[0][1], x[1]),
#                OPERATOR_MAP.items())))
# PATH_NEGATION = 'not'
# OPERATOR_NEGATION = '!'
# NEGATION = (PATH_NEGATION, OPERATOR_NEGATION)
# LOGICAL_AND = '&'
# LOGICAL_OR = ';'
# SEP_PATH = '.'
# SEP_VALUE = ','
# DIRECTIVE = ':'
# GROUP_BEGIN = '('
# GROUP_END = ')'
# 
# Path: armet/http/exceptions.py
# class BadRequest(BaseHTTPException):  # 400
#     status = client.BAD_REQUEST


import six
import operator
import sqlalchemy as sa
import functools
from functools import partial
from collections import Iterable
from copy import deepcopy
from six.moves import map, reduce
from armet.exceptions import ImproperlyConfigured
from armet.query import parser, Query, QuerySegment, constants
from armet.http.exceptions import BadRequest
from sqlalchemy.exc import InvalidRequestError
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, division


class ModelResourceOptions(object):

    def __init__(self, meta, name, bases):
        #! SQLAlchemy session used to perform operations on the models.
        self.Session = meta.get('Session')
        if not self.Session:
            raise ImproperlyConfigured(
                'A session factory (via sessionmaker) is required by '
                'the SQLAlchemy model connector.')


def ilike_helper(default):
    """Helper function that performs an `ilike` query if a string value
    is passed, otherwise the normal default operation."""
    @functools.wraps(default)
    def wrapped(x, y):
        # String values should use ILIKE queries.
        if isinstance(y, six.string_types) and not isinstance(x.type, sa.Enum):
            return x.ilike("%" + y + "%")
        else:
            return default(x, y)
    return wrapped


# Build an operator map to use for sqlalchemy.
OPERATOR_MAP = {

# Path: armet/exceptions.py
# class ImproperlyConfigured(BaseException):
#     """Something has been set up or configured incorrectly.
#     """
# 
# Path: armet/query/parser.py
# COMBINATORS = {
#     constants.LOGICAL_AND: operator.and_,
#     constants.LOGICAL_OR: operator.or_}
# OPERATOR_BEGIN_CHARS = set(x[0] for _, x in constants.OPERATORS if x)
# OPERATOR_SYMBOL_MAP = dict((v, k) for k, v in constants.OPERATORS if v)
# OPERATOR_KEYWORDS = set(k for k, _ in constants.OPERATORS)
# REVERSED_OPERATOR_SUFFIX_MAP = dict(
#     (v, k) for k, v in constants.OPERATOR_SUFFIX_MAP.items()
# )
# class Query(object):
# class QuerySegment(object):
# class NoopQuerySegment(object):
# class BinarySegmentCombinator(object):
# class UnarySegmentCombinator(object):
#     def __init__(self, original, parsed):
#     def __init__(self, **kwargs):
#     def __repr__(self):
#     def __str__(self):
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, left, right, operation=operator.and_):
#     def __repr__(self):
#     def __str__(self):
#     def __init__(self, operand, operation=operator.not_):
#     def __repr__(self):
#     def __str__(self):
# def parse(text, encoding='utf8'):
# def reset_stringio(buf):
# def split_segments(text, closing_paren=False):
# def combine(segments, combinators):
# def parse_directive(key):
# def parse_segment(text):
# 
# Path: armet/query/constants.py
# OPERATOR_EQUAL = 'exact', '=='
# OPERATOR_IEQUAL = 'iexact', '='
# OPERATOR_LT = 'lt', '<'
# OPERATOR_LTE = 'lte', '<='
# OPERATOR_GT = 'gt', '>'
# OPERATOR_GTE = 'gte', '>='
# OPERATOR_REGEX = 'regex', '*='
# OPERATOR_ISNULL = 'isnull', None
# OPERATOR_ICONTAINS = 'icontains', None
# OPERATOR_FALLBACK = OPERATOR_IEQUAL
# OPERATOR_SUFFIX_FALLBACK = OPERATOR_FALLBACK[0]
# OPERATOR_EQUALITY_FALLBACK = OPERATOR_FALLBACK[1]
# OPERATOR_MAP = {
#     OPERATOR_EQUAL: operator.eq,
#     OPERATOR_IEQUAL: lambda x, y: x.lower() == y.lower(),
#     OPERATOR_LT: operator.lt,
#     OPERATOR_LTE: operator.le,
#     OPERATOR_GT: operator.gt,
#     OPERATOR_GTE: operator.ge,
#     OPERATOR_REGEX: lambda x, y: re.search(y, x),
#     OPERATOR_ISNULL: lambda x: x is None,
#     OPERATOR_ICONTAINS: operator.contains,
# }
# OPERATORS = set(OPERATOR_MAP.keys())
# OPERATOR_SUFFIXES = set(filter(None, map(operator.itemgetter(0), OPERATORS)))
# OPERATOR_EQUALITIES = set(filter(None, map(operator.itemgetter(1), OPERATORS)))
# OPERATOR_SUFFIX_MAP = dict(
#     filter(operator.itemgetter(0),
#            map(lambda x: (x[0][0], x[1]),
#                OPERATOR_MAP.items())))
# OPERATOR_EQUALITY_MAP = dict(
#     filter(operator.itemgetter(0),
#            map(lambda x: (x[0][1], x[1]),
#                OPERATOR_MAP.items())))
# PATH_NEGATION = 'not'
# OPERATOR_NEGATION = '!'
# NEGATION = (PATH_NEGATION, OPERATOR_NEGATION)
# LOGICAL_AND = '&'
# LOGICAL_OR = ';'
# SEP_PATH = '.'
# SEP_VALUE = ','
# DIRECTIVE = ':'
# GROUP_BEGIN = '('
# GROUP_END = ')'
# 
# Path: armet/http/exceptions.py
# class BadRequest(BaseHTTPException):  # 400
#     status = client.BAD_REQUEST


# Path: armet/connectors/sqlalchemy/resources.py
import six
import operator
import sqlalchemy as sa
import functools
from functools import partial
from collections import Iterable
from copy import deepcopy
from six.moves import map, reduce
from armet.exceptions import ImproperlyConfigured
from armet.query import parser, Query, QuerySegment, constants
from armet.http.exceptions import BadRequest
from sqlalchemy.exc import InvalidRequestError

# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, division


class ModelResourceOptions(object):

    def __init__(self, meta, name, bases):
        #! SQLAlchemy session used to perform operations on the models.
        self.Session = meta.get('Session')
        if not self.Session:
            raise ImproperlyConfigured(
                'A session factory (via sessionmaker) is required by '
                'the SQLAlchemy model connector.')


def ilike_helper(default):
    """Helper function that performs an `ilike` query if a string value
    is passed, otherwise the normal default operation."""
    @functools.wraps(default)
    def wrapped(x, y):
        # String values should use ILIKE queries.
        if isinstance(y, six.string_types) and not isinstance(x.type, sa.Enum):
            return x.ilike("%" + y + "%")
        else:
            return default(x, y)
    return wrapped


# Build an operator map to use for sqlalchemy.
OPERATOR_MAP = {

    constants.OPERATOR_EQUAL: operator.eq,