<commit_before>import datetime
import functools
import hashlib
import itertools
import json
import logging
import time

from funcy import project
from flask_sqlalchemy import SQLAlchemy
from flask.ext.sqlalchemy import SignallingSession
from flask_login import UserMixin, AnonymousUserMixin
from sqlalchemy.dialects import postgresql
from sqlalchemy.event import listens_for
from sqlalchemy.types import TypeDecorator
from sqlalchemy.orm import object_session
# noinspection PyUnresolvedReferences
from sqlalchemy.orm.exc import NoResultFound

from passlib.apps import custom_app_context as pwd_context

from redash import redis_connection, utils
from redash.destinations import get_destination, get_configuration_schema_for_destination_type
from redash.permissions import has_access, view_only
from redash.query_runner import get_query_runner, get_configuration_schema_for_query_runner_type
from redash.utils import generate_token, json_dumps
from redash.utils.configuration import ConfigurationContainer

db = SQLAlchemy()
Column = functools.partial(db.Column, nullable=False)

# AccessPermission and Change use a 'generic foreign key' approach to refer to
# either queries or dashboards.
# TODO replace this with association tables.
_gfk_types = {}


class GFKBase(object):
    """
    Compatibility with 'generic foreign key' approach Peewee used.
    """
    # XXX Replace this with table-per-association.
    object_type = Column(db.String(255))
    object_id = Column(db.Integer)

    _object = None

    @property
    def object(self):
        session = object_session(self)
        if self._object or not session:
            return self._object
        else:
            object_class = _gfk_types[self.object_type]
            self._object = session.query(object_class).filter(
                object_class.id == self.object_id).first()
            return self._object

    @object.setter
    def object(self, value):
        self._object = value
        self.object_type = value.__class__.__tablename__
        self.object_id = value.id


# # Support for cast operation on database fields
# @peewee.Node.extend()
# def cast(self, as_type):
#     return peewee.Expression(self, '::', peewee.SQL(as_type))


class PseudoJSON(TypeDecorator):
    impl = db.Text
    def process_bind_param(self, value, dialect):
        return json_dumps(value)
    def process_result_value(self, value, dialect):
        if not value:
            return value
        return json.loads(value)


class TimestampMixin(object):
    updated_at = Column(db.DateTime(True), default=db.func.now(),
                           onupdate=db.func.now(), nullable=False)
    created_at = Column(db.DateTime(True), default=db.func.now(),
                           nullable=False)


class ChangeTrackingMixin(object):
    skipped_fields = ('id', 'created_at', 'updated_at', 'version')
    _clean_values = None

    def prep_cleanvalues(self):
        self.__dict__['_clean_values'] = {}
        for c in self.__class__.__table__.c:
            self._clean_values[c.name] = None

    def __setattr__(self, key, value):
        if self._clean_values is None:
            self.prep_cleanvalues()
        if key in self._clean_values:
            previous = getattr(self, key)
            self._clean_values[key] = previous

        super(ChangeTrackingMixin, self).__setattr__(key, value)

    def record_changes(self, changed_by):
        changes = {}
        for k, v in self._clean_values.iteritems():
            if k not in self.skipped_fields:
                changes[k] = {'previous': v, 'current': getattr(self, k)}
        db.session.flush()
        db.session.add(Change(object_type=self.__class__.__tablename__,
                           object=self,
                           object_version=self.version,
                           user=changed_by,
                           change=changes))


class ConflictDetectedError(Exception):
    pass


class BelongsToOrgMixin(object):
    @classmethod
    def get_by_id_and_org(cls, object_id, org):
        return db.session.query(cls).filter(cls.id == object_id, cls.org == org).one()


class PermissionsCheckMixin(object):
    def has_permission(self, permission):
        return self.has_permissions((permission,))

    def has_permissions(self, permissions):
        has_permissions = reduce(lambda a, b: a and b,
                                 map(lambda permission: permission in self.permissions,
                                     permissions),
                                 True)

        return has_permissions


class AnonymousUser(AnonymousUserMixin, PermissionsCheckMixin):
    @property
    def permissions(self):
        return []


class ApiUser(UserMixin, PermissionsCheckMixin):
    def __init__(self, api_key, org, groups, name=None):
        self.object = None
        if isinstance(api_key, basestring):
            self.id = api_key
            self.name = name
        else:
            self.id = api_key.api_key
            self.name = "ApiKey: {}".format(api_key.id)
            self.object = api_key.object
        self.groups = groups
        self.org = org
<commit_msg>Change groups property of ApiUser to be group_ids<commit_after>import datetime
import functools
import hashlib
import itertools
import json
import logging
import time

from funcy import project
from flask_sqlalchemy import SQLAlchemy
from flask.ext.sqlalchemy import SignallingSession
from flask_login import UserMixin, AnonymousUserMixin
from sqlalchemy.dialects import postgresql
from sqlalchemy.event import listens_for
from sqlalchemy.types import TypeDecorator
from sqlalchemy.orm import object_session
# noinspection PyUnresolvedReferences
from sqlalchemy.orm.exc import NoResultFound

from passlib.apps import custom_app_context as pwd_context

from redash import redis_connection, utils
from redash.destinations import get_destination, get_configuration_schema_for_destination_type
from redash.permissions import has_access, view_only
from redash.query_runner import get_query_runner, get_configuration_schema_for_query_runner_type
from redash.utils import generate_token, json_dumps
from redash.utils.configuration import ConfigurationContainer

db = SQLAlchemy()
Column = functools.partial(db.Column, nullable=False)

# AccessPermission and Change use a 'generic foreign key' approach to refer to
# either queries or dashboards.
# TODO replace this with association tables.
_gfk_types = {}


class GFKBase(object):
    """
    Compatibility with 'generic foreign key' approach Peewee used.
    """
    # XXX Replace this with table-per-association.
    object_type = Column(db.String(255))
    object_id = Column(db.Integer)

    _object = None

    @property
    def object(self):
        session = object_session(self)
        if self._object or not session:
            return self._object
        else:
            object_class = _gfk_types[self.object_type]
            self._object = session.query(object_class).filter(
                object_class.id == self.object_id).first()
            return self._object

    @object.setter
    def object(self, value):
        self._object = value
        self.object_type = value.__class__.__tablename__
        self.object_id = value.id


# # Support for cast operation on database fields
# @peewee.Node.extend()
# def cast(self, as_type):
#     return peewee.Expression(self, '::', peewee.SQL(as_type))


class PseudoJSON(TypeDecorator):
    impl = db.Text
    def process_bind_param(self, value, dialect):
        return json_dumps(value)
    def process_result_value(self, value, dialect):
        if not value:
            return value
        return json.loads(value)


class TimestampMixin(object):
    updated_at = Column(db.DateTime(True), default=db.func.now(),
                           onupdate=db.func.now(), nullable=False)
    created_at = Column(db.DateTime(True), default=db.func.now(),
                           nullable=False)


class ChangeTrackingMixin(object):
    skipped_fields = ('id', 'created_at', 'updated_at', 'version')
    _clean_values = None

    def prep_cleanvalues(self):
        self.__dict__['_clean_values'] = {}
        for c in self.__class__.__table__.c:
            self._clean_values[c.name] = None

    def __setattr__(self, key, value):
        if self._clean_values is None:
            self.prep_cleanvalues()
        if key in self._clean_values:
            previous = getattr(self, key)
            self._clean_values[key] = previous

        super(ChangeTrackingMixin, self).__setattr__(key, value)

    def record_changes(self, changed_by):
        changes = {}
        for k, v in self._clean_values.iteritems():
            if k not in self.skipped_fields:
                changes[k] = {'previous': v, 'current': getattr(self, k)}
        db.session.flush()
        db.session.add(Change(object_type=self.__class__.__tablename__,
                           object=self,
                           object_version=self.version,
                           user=changed_by,
                           change=changes))


class ConflictDetectedError(Exception):
    pass


class BelongsToOrgMixin(object):
    @classmethod
    def get_by_id_and_org(cls, object_id, org):
        return db.session.query(cls).filter(cls.id == object_id, cls.org == org).one()


class PermissionsCheckMixin(object):
    def has_permission(self, permission):
        return self.has_permissions((permission,))

    def has_permissions(self, permissions):
        has_permissions = reduce(lambda a, b: a and b,
                                 map(lambda permission: permission in self.permissions,
                                     permissions),
                                 True)

        return has_permissions


class AnonymousUser(AnonymousUserMixin, PermissionsCheckMixin):
    @property
    def permissions(self):
        return []


class ApiUser(UserMixin, PermissionsCheckMixin):
    def __init__(self, api_key, org, groups, name=None):
        self.object = None
        if isinstance(api_key, basestring):
            self.id = api_key
            self.name = name
        else:
            self.id = api_key.api_key
            self.name = "ApiKey: {}".format(api_key.id)
            self.object = api_key.object
        self.group_ids = groups
        self.org = org

{'fraction_non_alphanumeric': 0.08358382623872782, 'fraction_numerical': 0.0005817899738194512, 'mean_word_length': 2.778021978021978, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5663349', 'n_tokens_mistral': 2995, 'n_tokens_neox': 2905, 'n_words': 721}