<commit_before># -*- coding: utf-8 -*-

from itertools import repeat
from django.db import models, connections
from django.db.models.query import QuerySet

# Compatibility import and fixes section.

try:
    from django.utils.encoding import force_unicode as force_text
except ImportError:
    from django.utils.encoding import force_text

try:
    from django.db.transaction import atomic
except ImportError:
    # This encapsulates pre django 1.6 transaction
    # behavior under same abstraction as django 1.6 atomic
    # decorator. This not intends to emulate django 1.6 atomic
    # behavior, only has partially same interface for easy
    # use.
    from django.db import transaction

    class atomic(object):
        def __init__(self, using=None):
            self.using = using

        def __enter__(self):
            if not transaction.is_managed(using=self.using):
                transaction.enter_transaction_management(using=self.using)
                self.forced_managed = True
            else:
                self.forced_managed = False

        def __exit__(self, *args, **kwargs):
            try:
                if self.forced_managed:
                    transaction.commit(using=self.using)
                else:
                    transaction.commit_unless_managed(using=self.using)
            finally:
                if self.forced_managed:
                    transaction.leave_transaction_management(using=self.using)


def auto_update_search_field_handler(sender, instance, *args, **kwargs):
    instance.update_search_field()


class SearchManagerMixIn(object):
    """
    A mixin to create a Manager with a 'search' method that may do a full text search
    on the model.

    The manager is set up with a list of one or more model's fields that will be searched.
    It can be a list of field names, or a list of tuples (field_name, weight). It can also
    be None, in that case every CharField and TextField in the model will be searched.

    You can also give a 'search_field', a VectorField into where the values of the searched
    fields are copied and normalized. If you give it, the searches will be made on this
    field; if not, they will be made directly in the searched fields.

    When using search_field, if auto_update = True, Django signals will be used to
    automatically syncronize the search_field with the searched fields every time instances
    are saved. If not, you can call to 'update_search_field' method in model instances to do this.
    If search_field not used, both auto_update and update_search_field does nothing. Alternatively,
    you can create a postgresql trigger to do the syncronization at database level, see this:

    http://www.postgresql.org/docs/9.1/interactive/textsearch-features.html#TEXTSEARCH-UPDATE-TRIGGERS

    In both cases, you should create a text search index, on either the searched fields or
    the compound search_field, like explained here:

    http://www.postgresql.org/docs/9.1/interactive/textsearch-tables.html#TEXTSEARCH-TABLES-INDEX

    Finally, you can give a 'config', the Postgres text search configuration that will be used
    to normalize the search_field and the queries. How do you can create a configuration:

    http://www.postgresql.org/docs/9.1/interactive/textsearch-configuration.html

    To do all those actions in database, create a setup sql script for Django:

    https://docs.djangoproject.com/en/1.4/howto/initial-data/#providing-initial-sql-data
    """

    def __init__(self, fields=None, search_field='search_index',
            config='pg_catalog.english', auto_update_search_field=False):
        self.search_field = search_field
        self.default_weight = 'D'
        self.config = config
        self.auto_update_search_field = auto_update_search_field
        self._fields = fields

        super(SearchManagerMixIn, self).__init__()

    def contribute_to_class(self, cls, name):
        '''
        Called automatically by Django when setting up the model class.
        '''

        # Attach this manager as _fts_manager in the model class.
        if not getattr(cls, '_fts_manager', None):
            cls._fts_manager = self

        # Add 'update_search_field' instance method, that calls manager's update_search_field.
        if not getattr(cls, 'update_search_field', None):
            def update_search_field(self, using=None, config=None):
                self._fts_manager.update_search_field(pk=self.pk, using=using, config=config)
            setattr(cls, 'update_search_field', update_search_field)

        if self.auto_update_search_field:
            models.signals.post_save.connect(auto_update_search_field_handler, sender=cls)

        super(SearchManagerMixIn, self).contribute_to_class(cls, name)

    def get_query_set(self):
        return SearchQuerySet(model=self.model, using=self._db)

    def search(self, *args, **kwargs):
        return self.get_query_set().search(*args, **kwargs)

    def update_search_field(self, pk=None, config=None, using=None):
        '''
        Update the search_field of one instance, or a list of instances, or
        all instances in the table (pk is one key, a list of keys or none).

        If there is no search_field, this function does nothing.
        '''

        if not self.search_field:<commit_msg>Support for abstract parent classes<commit_after># -*- coding: utf-8 -*-

from itertools import repeat
from django.db import models, connections
from django.db.models.query import QuerySet

# Compatibility import and fixes section.

try:
    from django.utils.encoding import force_unicode as force_text
except ImportError:
    from django.utils.encoding import force_text

try:
    from django.db.transaction import atomic
except ImportError:
    # This encapsulates pre django 1.6 transaction
    # behavior under same abstraction as django 1.6 atomic
    # decorator. This not intends to emulate django 1.6 atomic
    # behavior, only has partially same interface for easy
    # use.
    from django.db import transaction

    class atomic(object):
        def __init__(self, using=None):
            self.using = using

        def __enter__(self):
            if not transaction.is_managed(using=self.using):
                transaction.enter_transaction_management(using=self.using)
                self.forced_managed = True
            else:
                self.forced_managed = False

        def __exit__(self, *args, **kwargs):
            try:
                if self.forced_managed:
                    transaction.commit(using=self.using)
                else:
                    transaction.commit_unless_managed(using=self.using)
            finally:
                if self.forced_managed:
                    transaction.leave_transaction_management(using=self.using)


def auto_update_search_field_handler(sender, instance, *args, **kwargs):
    instance.update_search_field()


class SearchManagerMixIn(object):
    """
    A mixin to create a Manager with a 'search' method that may do a full text search
    on the model.

    The manager is set up with a list of one or more model's fields that will be searched.
    It can be a list of field names, or a list of tuples (field_name, weight). It can also
    be None, in that case every CharField and TextField in the model will be searched.

    You can also give a 'search_field', a VectorField into where the values of the searched
    fields are copied and normalized. If you give it, the searches will be made on this
    field; if not, they will be made directly in the searched fields.

    When using search_field, if auto_update = True, Django signals will be used to
    automatically syncronize the search_field with the searched fields every time instances
    are saved. If not, you can call to 'update_search_field' method in model instances to do this.
    If search_field not used, both auto_update and update_search_field does nothing. Alternatively,
    you can create a postgresql trigger to do the syncronization at database level, see this:

    http://www.postgresql.org/docs/9.1/interactive/textsearch-features.html#TEXTSEARCH-UPDATE-TRIGGERS

    In both cases, you should create a text search index, on either the searched fields or
    the compound search_field, like explained here:

    http://www.postgresql.org/docs/9.1/interactive/textsearch-tables.html#TEXTSEARCH-TABLES-INDEX

    Finally, you can give a 'config', the Postgres text search configuration that will be used
    to normalize the search_field and the queries. How do you can create a configuration:

    http://www.postgresql.org/docs/9.1/interactive/textsearch-configuration.html

    To do all those actions in database, create a setup sql script for Django:

    https://docs.djangoproject.com/en/1.4/howto/initial-data/#providing-initial-sql-data
    """

    def __init__(self, fields=None, search_field='search_index',
            config='pg_catalog.english', auto_update_search_field=False):
        self.search_field = search_field
        self.default_weight = 'D'
        self.config = config
        self.auto_update_search_field = auto_update_search_field
        self._fields = fields

        super(SearchManagerMixIn, self).__init__()

    def contribute_to_class(self, cls, name):
        '''
        Called automatically by Django when setting up the model class.
        '''
        if not cls._meta.abstract:
            # Attach this manager as _fts_manager in the model class.
            if not getattr(cls, '_fts_manager', None):
                cls._fts_manager = self
    
            # Add 'update_search_field' instance method, that calls manager's update_search_field.
            if not getattr(cls, 'update_search_field', None):
                def update_search_field(self, using=None, config=None):
                    self._fts_manager.update_search_field(pk=self.pk, using=using, config=config)
                setattr(cls, 'update_search_field', update_search_field)
    
            if self.auto_update_search_field:
                models.signals.post_save.connect(auto_update_search_field_handler, sender=cls)

        super(SearchManagerMixIn, self).contribute_to_class(cls, name)

    def get_query_set(self):
        return SearchQuerySet(model=self.model, using=self._db)

    def search(self, *args, **kwargs):
        return self.get_query_set().search(*args, **kwargs)

    def update_search_field(self, pk=None, config=None, using=None):
        '''
        Update the search_field of one instance, or a list of instances, or
        all instances in the table (pk is one key, a list of keys or none).

        If there is no search_field, this function does nothing.
        '''

        if not self.search_field:
{'fraction_non_alphanumeric': 0.07205280773521755, 'fraction_numerical': 0.0027891409445890665, 'mean_word_length': 2.9130592942888325, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '783779', 'n_tokens_mistral': 2964, 'n_tokens_neox': 2849, 'n_words': 1004}