<commit_before>from __future__ import absolute_import  # this module has a file called 'xml'
from abc import ABCMeta, abstractmethod

import six

from xml.etree import ElementTree
from casexml.apps.phone.models import OTARestoreUser
from casexml.apps.case.xml import V1, V2
from django.conf import settings
from dimagi.utils.modules import to_function
import itertools


class FixtureProvider(six.with_metaclass(ABCMeta)):

    @abstractmethod
    def __call__(self, restore_state):
        raise NotImplementedError


class FixtureGenerator(object):
    """
    The generator object, which gets fixtures from your config file that should
    be included when OTA restoring.
    
    See: https://bitbucket.org/javarosa/javarosa/wiki/externalinstances
    
    To use, add the following to your settings.py
    
    FIXTURE_GENERATORS = {
        'group1': [
           "myapp.fixturegenerators.gen1",
           "myapp.fixturegenerators.gen2",
            ...
        ],
        ...
    }
    
    The values in the file should be paths to objects that
    implement the following API:
    
    provider(user, version, last_sync) --> [list of fixture objects]
    provider.id --> the ID of the fixture

    If the provider generates multiple fixtures it should use an ID format as follows:
        "prefix:dynamic"
    In this case 'provider.id' should just be the ID prefix.
    
    The function should return an empty list if there are no fixtures
    """

    def __init__(self):
        self._generator_providers = {}
        if hasattr(settings, "FIXTURE_GENERATORS"):
            for group, func_paths in settings.FIXTURE_GENERATORS.items():
                self._generator_providers[group] = filter(None, [
                    to_function(func_path, failhard=True) for func_path in func_paths
                ])

    def get_providers(self, user, fixture_id=None, version=V2):
        if version == V1:
            return []  # V1 phones will never use or want fixtures

        if not isinstance(user, OTARestoreUser):
            return []

        providers = list(itertools.chain(*self._generator_providers.values()))

        if fixture_id:
            full_id = fixture_id
            prefix = fixture_id.split(':', 1)[0]

            def provider_matches(provider):
                # some providers generate fixtures with dynamic ID's e.g. item-list:my-item-list
                # in which case provider.id is just the prefix.
                return provider.id == full_id or provider.id == prefix

            providers = [provider for provider in providers if provider_matches(provider)]

        return providers

    def _get_fixtures(self, restore_user, fixture_id=None):
        providers = self.get_providers(restore_user, fixture_id=fixture_id)
        return itertools.chain(*[
            provider(_get_restore_state(restore_user))
            for provider in providers
        ])

    def get_fixture_by_id(self, fixture_id, restore_user):
        """
        Only get fixtures with the specified ID.
        """
        fixtures = self._get_fixtures(restore_user, fixture_id)
        for fixture in fixtures:
            if isinstance(fixture, basestring):
                cached_fixtures = ElementTree.fromstring(
                    "<cached-fixture>{}</cached-fixture>".format(fixture)
                )
                for fixture in cached_fixtures:
                    if fixture.attrib.get("id") == fixture_id:
                        return fixture
            if fixture.attrib.get("id") == fixture_id:
                return fixture

    def get_fixtures(self, restore_user):
        """
        Gets all fixtures associated with an OTA restore operation
        """
        return self._get_fixtures(restore_user)


generator = FixtureGenerator()


def _get_restore_state(restore_user):
    from casexml.apps.phone.restore import RestoreState
    from casexml.apps.phone.restore import RestoreParams
    params = RestoreParams(version=V2)
    return RestoreState(
        restore_user.project,
        restore_user,
        params,
        async=False,
        overwrite_cache=False
    )
<commit_msg>add comment<commit_after>from __future__ import absolute_import  # this module has a file called 'xml'
from abc import ABCMeta, abstractmethod

import six

from xml.etree import ElementTree
from casexml.apps.phone.models import OTARestoreUser
from casexml.apps.case.xml import V1, V2
from django.conf import settings
from dimagi.utils.modules import to_function
import itertools


class FixtureProvider(six.with_metaclass(ABCMeta)):

    @abstractmethod
    def __call__(self, restore_state):
        raise NotImplementedError


class FixtureGenerator(object):
    """
    The generator object, which gets fixtures from your config file that should
    be included when OTA restoring.
    
    See: https://bitbucket.org/javarosa/javarosa/wiki/externalinstances
    
    To use, add the following to your settings.py
    
    FIXTURE_GENERATORS = {
        'group1': [
           "myapp.fixturegenerators.gen1",
           "myapp.fixturegenerators.gen2",
            ...
        ],
        ...
    }
    
    The values in the file should be paths to objects that
    implement the following API:
    
    provider(user, version, last_sync) --> [list of fixture objects]
    provider.id --> the ID of the fixture

    If the provider generates multiple fixtures it should use an ID format as follows:
        "prefix:dynamic"
    In this case 'provider.id' should just be the ID prefix.
    
    The function should return an empty list if there are no fixtures
    """

    def __init__(self):
        self._generator_providers = {}
        if hasattr(settings, "FIXTURE_GENERATORS"):
            for group, func_paths in settings.FIXTURE_GENERATORS.items():
                self._generator_providers[group] = filter(None, [
                    to_function(func_path, failhard=True) for func_path in func_paths
                ])

    def get_providers(self, user, fixture_id=None, version=V2):
        if version == V1:
            return []  # V1 phones will never use or want fixtures

        if not isinstance(user, OTARestoreUser):
            return []

        providers = list(itertools.chain(*self._generator_providers.values()))

        if fixture_id:
            full_id = fixture_id
            prefix = fixture_id.split(':', 1)[0]

            def provider_matches(provider):
                # some providers generate fixtures with dynamic ID's e.g. item-list:my-item-list
                # in which case provider.id is just the prefix.
                return provider.id == full_id or provider.id == prefix

            providers = [provider for provider in providers if provider_matches(provider)]

        return providers

    def _get_fixtures(self, restore_user, fixture_id=None):
        providers = self.get_providers(restore_user, fixture_id=fixture_id)
        return itertools.chain(*[
            provider(_get_restore_state(restore_user))
            for provider in providers
        ])

    def get_fixture_by_id(self, fixture_id, restore_user):
        """
        Only get fixtures with the specified ID.
        """
        fixtures = self._get_fixtures(restore_user, fixture_id)
        for fixture in fixtures:
            if isinstance(fixture, basestring):
                # could be a string if it's coming from cache
                cached_fixtures = ElementTree.fromstring(
                    "<cached-fixture>{}</cached-fixture>".format(fixture)
                )
                for fixture in cached_fixtures:
                    if fixture.attrib.get("id") == fixture_id:
                        return fixture
            if fixture.attrib.get("id") == fixture_id:
                return fixture

    def get_fixtures(self, restore_user):
        """
        Gets all fixtures associated with an OTA restore operation
        """
        return self._get_fixtures(restore_user)


generator = FixtureGenerator()


def _get_restore_state(restore_user):
    from casexml.apps.phone.restore import RestoreState
    from casexml.apps.phone.restore import RestoreParams
    params = RestoreParams(version=V2)
    return RestoreState(
        restore_user.project,
        restore_user,
        params,
        async=False,
        overwrite_cache=False
    )

{'fraction_non_alphanumeric': 0.07625899280575539, 'fraction_numerical': 0.0026378896882494006, 'mean_word_length': 2.363306451612903, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 14}, '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': '249564', 'n_tokens_mistral': 2289, 'n_tokens_neox': 2226, 'n_words': 648}