<commit_before>"""
OPAL model utils
"""

from django.contrib.contenttypes import generic
from django.db import models

from opal.utils.fields import ForeignKeyOrFreeText

class LookupList(models.Model):
    class Meta:
        abstract = True


def lookup_list(name, module=__name__):
    """
    Given the name of a lookup list, return the tuple of class_name, bases, attrs
    for the user to define the class
    """
    prefix = 'Lookup List: '
    class_name = name.capitalize() # TODO handle camelcase properly
    bases = (LookupList,)
    attrs = {
        'name': models.CharField(max_length=255, unique=True),
        'synonyms': generic.GenericRelation('opal.Synonym'),
        'Meta': type('Meta', (object,), {'ordering': ['name'], 
                                         'verbose_name': prefix+name}),
        '__unicode__': lambda self: self.name,
        '__module__': module,
    }
    return class_name, bases, attrs
    
 
def episode_subrecords():
    """
    Generator function for episode subrecords.
    """
    # CircularImport - SELF is used as a manager by models in this module
    from opal.models import EpisodeSubrecord
    for model in EpisodeSubrecord.__subclasses__():
        if model._meta.abstract:
            continue
        yield model

def patient_subrecords():
    """
    Generator function for patient subrecords.
    """
    # CircularImport - SELF is used as a manager by models in this module
    from opal.models import PatientSubrecord
    for model in PatientSubrecord.__subclasses__():
        if model._meta.abstract:
            continue
        yield model

def subrecords():
    """
    Generator function for subrecords
    """<commit_msg>Iterate through subclasses of subclasses recursively.<commit_after>"""
OPAL model utils
"""

from django.contrib.contenttypes import generic
from django.db import models

from opal.utils.fields import ForeignKeyOrFreeText

class LookupList(models.Model):
    class Meta:
        abstract = True


def lookup_list(name, module=__name__):
    """
    Given the name of a lookup list, return the tuple of class_name, bases, attrs
    for the user to define the class
    """
    prefix = 'Lookup List: '
    class_name = name.capitalize() # TODO handle camelcase properly
    bases = (LookupList,)
    attrs = {
        'name': models.CharField(max_length=255, unique=True),
        'synonyms': generic.GenericRelation('opal.Synonym'),
        'Meta': type('Meta', (object,), {'ordering': ['name'], 
                                         'verbose_name': prefix+name}),
        '__unicode__': lambda self: self.name,
        '__module__': module,
    }
    return class_name, bases, attrs
    
 
def _itersubclasses(cls, _seen=None):
    """
    Recursively iterate through subclasses
    """
    if not isinstance(cls, type):
        raise TypeError('itersubclasses must be called with '
                        'new-style classes, not %.100r' % cls)
    if _seen is None: _seen = set()
    try:
        subs = cls.__subclasses__()
    except TypeError: # fails only when cls is type
        subs = cls.__subclasses__(cls)
    for sub in subs:
        if sub not in _seen:
            _seen.add(sub)
            yield sub
            for sub in _itersubclasses(sub, _seen):
                yield sub

def episode_subrecords():
    """
    Generator function for episode subrecords.
    """
    # CircularImport - SELF is used as a manager by models in this module
    from opal.models import EpisodeSubrecord
    for model in _itersubclasses(EpisodeSubrecord):
        if model._meta.abstract:
            continue
        yield model

def patient_subrecords():
    """
    Generator function for patient subrecords.
    """
    # CircularImport - SELF is used as a manager by models in this module
    from opal.models import PatientSubrecord
    for model in _itersubclasses(PatientSubrecord):
        if model._meta.abstract:
            continue
        yield model

def subrecords():
    """
    Generator function for subrecords
    """
{'fraction_non_alphanumeric': 0.0903912828132739, 'fraction_numerical': 0.002228826151560178, 'mean_word_length': 2.558590308370044, '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': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1162143', 'n_tokens_mistral': 1126, 'n_tokens_neox': 1100, 'n_words': 365}