Ejemplo n.º 1
0
def disable_signal_handler(signal_ref_path, handler, uid, limit_to_classes):
    for cls in filter_classes(limit_to_classes):
        resolve_module_attr(signal_ref_path).disconnect(handler, cls, True, uid)

    disable_signal_handler_for_celery.apply_async(
        args=(signal_ref_path, full_module_path(handler), uid, map(lambda cls: full_module_path(cls), limit_to_classes)),
        soft_time_limit=3600,
        time_limit=3600,
        countdown=1
    )
Ejemplo n.º 2
0
        def sql_map(path):
            """
                Like field_map, but instead produces a sql version of the mapping.
                Since our export functionality sends sql to ogr, we need to handle
                all the formatting in the sql. We can do this by overriding the normal
                select value with a function (e.g. select updated_date becomes select format(update_data).
                In order to do this we create an extra clause for the Django QuerySet since it sadly
                doesn't have a built-in ability to decorate selections with functions
            :param path:
            :param field_class_path:
            :return: An array with two values, the path and the mapping. If no mapping
            is needed, an array with [path, path] is returned. This way the path is used as an extra
            value and the column order is preserved. Django nonsensically puts the extra fields before
            the normal fields, even if their names match. Apparently they didn't considered the situation
            of replacing a normal column select with a formatted column select, or they don't expect
            raw sql to be used.
            """

            full_column_name = '%s.%s' % (field_to_table_lookup.get(path), path.split('__')[-1]) if field_to_table_lookup.get(path) else path
            field_class_path = self.result_map.field_lookup.get(path)
            if not field_class_path:
                return None
            resolved_field_class = resolve_module_attr(field_class_path)
            if resolved_field_class and issubclass(resolved_field_class, DateField):
                # Format the date to match our prefered style (gotta love SQL :< )
                return [path,
                        "to_char({0}, 'YYYY-MM-DD') || 'T' || to_char({0}, 'HH:MI:SS') || to_char(extract(TIMEZONE_HOUR FROM {0}), 'fm00') || ':' || to_char(extract(TIMEZONE_MINUTE FROM {0}), 'fm00')".format(full_column_name)]
            return None
Ejemplo n.º 3
0
        def related_field_map(field_path, related_model_class_path):
            """
                This helps join feature_classes resolve related models,
                such as BuiltForms. The join feature class can't do this
                through the query since we have to use a values() query.
                Thus the query only contains built_form_id and not built_form with a
                reference to the built_form instance. The API needs to be
                able to return a property built_form=uri so we tell it how do
                do that here.
            :param field_path:
            :param related_model_class_path:
            :return: a key and value, the key is the escaped path (using _x_ for __) with the
             final _id removed and the value is
            the related model class. Ex: built_form_id results in
            [built_form, BuiltForm] for the main model or geographies_[scope_id]_end_state_feature__built_form_id in
            [geographies_[scope_id]_x_end_state_feature_x_built_form, BuiltForm]
            """

            # For static classes you module name resolution. For dynamic classes rely on current truth
            # that dynamic classes are all created in the main app, since module name resolution relies
            # on static module files
            logger.debug("Resolving %s" % related_model_class_path if\
                'dynamic_subclassing' not in related_model_class_path else\
                'main.%s' % related_model_class_path.split('.')[-1])

            related_model_class = resolve_module_attr(related_model_class_path)

            escaped_field_path = field_path.replace(r'_id$',
                                                    '').replace('__', '_x_')
            return [escaped_field_path, related_model_class]
def get_dynamic_resource_class(super_class,
                               model_class,
                               fields={},
                               meta_fields={}):
    """
        Subclass the super_class and change the queryset to the model_class's and abstract to False
    :param super_class: The Resource class to subclass
    :param model_class: The concrete subclassed model class which we want the subclassed Resource class to query
    :param fields: Additional fields to give the class.
    :param meta_fields: Additional meta fields, such as fields or excludes. You can also pass
    a query_set here to use something more specific than the model_class's default QuerySet
    :return:
    """
    class_name = get_dynamic_resource_class_name(super_class, model_class)
    if not meta_fields.get('queryset'):
        # Find the matching resource in the cache if there are no meta_fields that would mutate it
        # Return the class if it was already created
        modname = globals()['__name__']
        existing_class = resolve_module_attr('%s.%s' % (modname, class_name))
        if existing_class:
            return existing_class

    logger.info(
        "Creating Resource Class %s for model class %s with the super class %s"
        % (class_name, model_class.__name__, super_class.__name__))
    queryset = model_class.objects.all()
    meta_attributes = merge(dict(queryset=queryset, abstract=False),
                            meta_fields)
    meta_class = type('Meta', (super_class.Meta, ), meta_attributes)
    resource_attributes = dict(Meta=meta_class, **fields)
    resource_class = ModelDeclarativeMetaclass(class_name, (super_class, ),
                                               resource_attributes)
    return resource_class
Ejemplo n.º 5
0
        def related_field_map(field_path, related_model_class_path):
            """
                This helps join feature_classes resolve related models,
                such as BuiltForms. The join feature class can't do this
                through the query since we have to use a values() query.
                Thus the query only contains built_form_id and not built_form with a
                reference to the built_form instance. The API needs to be
                able to return a property built_form=uri so we tell it how do
                do that here.
            :param field_path:
            :param related_model_class_path:
            :return: a key and value, the key is the escaped path (using _x_ for __) with the
             final _id removed and the value is
            the related model class. Ex: built_form_id results in
            [built_form, BuiltForm] for the main model or geographies_[scope_id]_end_state_feature__built_form_id in
            [geographies_[scope_id]_x_end_state_feature_x_built_form, BuiltForm]
            """

            # For static classes you module name resolution. For dynamic classes rely on current truth
            # that dynamic classes are all created in the main app, since module name resolution relies
            # on static module files
            logger.debug("Resolving %s" % related_model_class_path if\
                'dynamic_subclassing' not in related_model_class_path else\
                'main.%s' % related_model_class_path.split('.')[-1])

            related_model_class = resolve_module_attr(related_model_class_path)

            escaped_field_path = field_path.replace(r'_id$', '').replace('__', '_x_')
            return [escaped_field_path, related_model_class]
Ejemplo n.º 6
0
 def resolve_abstract_feature_class(self):
     """
         Extracts the abstract Feature class from the DbEntity configuration
         :return The abstract class or None if not configured
     """
     abstract_class_path = self.feature_class_configuration.abstract_class_name or None
     return resolve_module_attr(abstract_class_path) if abstract_class_path else None
Ejemplo n.º 7
0
def get_dynamic_resource_class(super_class, model_class, fields={}, meta_fields={}):
    """
        Subclass the super_class and change the queryset to the model_class's and abstract to False
    :param super_class: The Resource class to subclass
    :param model_class: The concrete subclassed model class which we want the subclassed Resource class to query
    :param fields: Additional fields to give the class.
    :param meta_fields: Additional meta fields, such as fields or excludes. You can also pass
    a query_set here to use something more specific than the model_class's default QuerySet
    :return:
    """
    class_name = get_dynamic_resource_class_name(super_class, model_class)
    if not meta_fields.get('queryset'):
        # Find the matching resource in the cache if there are no meta_fields that would mutate it
        # Return the class if it was already created
        modname = globals()['__name__']
        existing_class = resolve_module_attr('%s.%s' % (modname, class_name))
        if existing_class:
            return existing_class

    logger.info("Creating Resource Class %s for model class %s with the super class %s" % (class_name, model_class.__name__, super_class.__name__))
    queryset = model_class.objects.all()
    meta_attributes = merge(dict(queryset=queryset, abstract=False), meta_fields)
    meta_class = type('Meta', (super_class.Meta,), meta_attributes)
    resource_attributes = dict(Meta=meta_class, **fields)
    resource_class = ModelDeclarativeMetaclass(class_name, (super_class,), resource_attributes)
    return resource_class
Ejemplo n.º 8
0
 def dynamic_model_class_creator(cls):
     """
         Resolve the DynamicModelClass creator subclass instance that created this class
     :return:
     """
     if cls.Meta.abstract:
         raise Exception("Cannot resolve a DynamicModelClassCreator instance for an abstract class.")
     return resolve_module_attr(cls.dynamic_model_class_creator_class).from_dynamic_model_class(cls)
Ejemplo n.º 9
0
 def dynamic_model_class_creator(cls):
     """
         Resolve the DynamicModelClass creator subclass instance that created this class
     :return:
     """
     if cls.Meta.abstract:
         raise Exception("Cannot resolve a DynamicModelClassCreator instance for an abstract class.")
     return resolve_module_attr(cls.dynamic_model_class_creator_class).from_dynamic_model_class(cls)
Ejemplo n.º 10
0
 def resolve_abstract_feature_class(self):
     """
         Extracts the abstract Feature class from the DbEntity configuration
         :return The abstract class or None if not configured
     """
     abstract_class_path = self.feature_class_configuration.abstract_class_name or None
     return resolve_module_attr(
         abstract_class_path) if abstract_class_path else None
    def dynamic_join_model_class(self, join_models, related_field_names):
        """
            Creates an unmanaged subclass of the feature class with extra fields to represent the
            the fields of the join_models. This also adds fields for any fields specified in the
            related_model_lookup. This is not for join models but ForeignKeys such as BuiltForm
            These latter fields must be specified explicitly because the main model and join models
            can't populate their foreign keys from the query because the query has to be
            a ValuesQuerySet in order to do the join. So we create id versions of the fields here
            (e.g. built_form_id) which the query can fill and then use that to manually
            set the foreign key reference in the Tastypie resource.
            :param join_models: Other feature models whose attributes should be added to the subclass
            :param related_field_names: List of field names of foreign key id fields (AutoFields)

        """
        main_model_class = self.dynamic_model_class()
        manager = main_model_class.objects
        # Exclude the following field types. Since the base Feature defines an id we'll still get that, which we want
        exclude_field_types = (ForeignKey, ToManyField, OneToOneField, GeometryField)
        all_field_paths_to_fields = merge(
            # Create fields to represented foreign key id fields
            # Our query fetches these ids since it can't fetch related objects (since its a values() query)
            map_to_dict(
                lambda field_name: [field_name.replace('__', '_x_'),
                                    IntegerField(field_name.replace('__', '_x_'), null=True)],
                related_field_names
            ),
            # The join fields for each joined related model
            *map(
                lambda related_model: related_field_paths_to_fields(
                    manager,
                    related_model,
                    exclude_field_types=exclude_field_types,
                    fields=limited_api_fields(related_model),
                    separator='_x_'),
                join_models)
        )

        abstract_feature_class = resolve_module_attr(self.configuration.abstract_class_name)
        # Make sure the class name is unique to the related models and the given ConfigEntity
        related_models_unique_id = '_'.join(sorted(map(lambda related_model: related_model.__name__, join_models), ))
        dynamic_model_clazz = dynamic_model_class(
            main_model_class,
            self.db_entity.schema,
            self.db_entity.table,
            class_name="{0}{1}{2}{3}Join".format(
                abstract_feature_class.__name__,
                self.db_entity.id,
                self.config_entity.id,
                related_models_unique_id),
            fields=all_field_paths_to_fields,
            class_attrs=self.configuration.class_attrs or {},
            related_class_lookup=self.configuration.related_class_lookup or {},
            is_managed=False,
            cacheable=False)
        logger.info("Created dynamic join model class %s" % dynamic_model_clazz)
        logger.debug("Created with model fields %s" % map(lambda field: field.name, dynamic_model_clazz._meta.fields))
        logger.debug("Created with related and join fields %s" % all_field_paths_to_fields)
        return dynamic_model_clazz
 def dynamic_geography_class_name(self, geography_scope_id=None):
     """
         Returns the Geography class for the given config_entity scope, or by default the scope
         of this feature_class_configuration, which is that of its config_entity
     :param geography_scope_id: Optional config_entity scope id for which to fetch the Geography class
     :return:
     """
     return get_dynamic_model_class_name(resolve_module_attr('footprint.main.models.geographies.geography.Geography'),
                                         geography_scope_id or self.geography_scope.id)
Ejemplo n.º 13
0
 def create_class_property(class_attr):
     related_attr = class_attr.split('__')[0]
     related_class_name = related_class_lookup.get(related_attr, None)
     if not related_class_name:
         raise Exception("Expected related_class_lookup to contain %s, since class_attrs contain %s" % (related_attr, class_attr) )
     related_class = resolve_module_attr(related_class_name)
     # Create the getter property that uses the class manager to lookup up the related model by id
     def getter(cls):
         return related_class.objects.get(id=getattr(cls, class_attr))
     return ClassProperty(classmethod(getter))
 def key_to_abstract_model_class_lookup(cls, configuration_or_containers):
     """
         Like self.db_entity_key_to_feature_class_lookup, but used when no ConfigEntity is in scope.
         This returns the abstract version of the Feature subclasses by self.db_entity_key
     :param configuration_or_containers:
     :return:
     """
     return map_to_dict(lambda configuration: [configuration.key,
                                               resolve_module_attr(configuration.abstract_class_name)],
                        map(lambda configuration: cls.resolve_configuration(configuration), configuration_or_containers))
 def key_to_abstract_model_class_lookup(cls, configuration_or_containers):
     """
         Like self.db_entity_key_to_feature_class_lookup, but used when no ConfigEntity is in scope.
         This returns the abstract version of the Feature subclasses by self.db_entity_key
     :param configuration_or_containers:
     :return:
     """
     return map_to_dict(lambda configuration: [configuration.key,
                                               resolve_module_attr(configuration.abstract_class_name)],
                        map(lambda configuration: cls.resolve_configuration(configuration), configuration_or_containers))
Ejemplo n.º 16
0
    def clazz(self):
        """
            Reconsitutes the class from the class_name
        """
        try:
            clazz = resolve_module_attr(self.class_path)
        except AttributeError:
            # Dynamic subclasses can't resolve using a module path
            clazz = resolve_model(self.model_path)

        if not clazz:
            raise Exception("No class could be resolved for %s or %s" % (self.class_path, self.model_path))
        return clazz
Ejemplo n.º 17
0
 def init(self):
     for analysis_tool_configuration in self.analysis_module_configuration.analysis_tools:
         analysis_tool_class = resolve_module_attr(analysis_tool_configuration.get('class_name'))
         analysis_tool_class._no_post_save_publishing = True
         analysis_tool, created, updated = analysis_tool_class.objects.update_or_create(
             config_entity=self.config_entity,
             key=analysis_tool_configuration['key'],
             defaults=remove_keys(analysis_tool_configuration, ['key', 'class_name'])
         )
         analysis_tool.initialize(created)
         analysis_tool_class._no_post_save_publishing = False
         if not analysis_tool in self.analysis_tools.all():
             self.analysis_tools.add(analysis_tool)
Ejemplo n.º 18
0
 def init(self):
     for analysis_tool_configuration in self.analysis_module_configuration.analysis_tools:
         analysis_tool_class = resolve_module_attr(
             analysis_tool_configuration.get('class_name'))
         analysis_tool_class._no_post_save_publishing = True
         analysis_tool, created, updated = analysis_tool_class.objects.update_or_create(
             config_entity=self.config_entity,
             key=analysis_tool_configuration['key'],
             defaults=remove_keys(analysis_tool_configuration,
                                  ['key', 'class_name']))
         analysis_tool.initialize(created)
         analysis_tool_class._no_post_save_publishing = False
         if not analysis_tool in self.analysis_tools.all():
             self.analysis_tools.add(analysis_tool)
    def dynamic_geography_class(self, geography_scope=None):
        """
            Return the geography class based on the db_entity or config_entity
            :param geography_scope. Optional geography_scope override. By default
            self.geogrpahy_scope is used
        """
        scope = geography_scope or self.geography_scope

        return dynamic_model_class(
            resolve_module_attr('footprint.main.models.geographies.geography.Geography'),
            scope.schema(),
            'geography',
            self.dynamic_geography_class_name(geography_scope.id if geography_scope else None),
            scope=scope
        )
Ejemplo n.º 20
0
            def create_class_property(class_attr):
                related_attr = class_attr.split('__')[0]
                related_class_name = related_class_lookup.get(
                    related_attr, None)
                if not related_class_name:
                    raise Exception(
                        "Expected related_class_lookup to contain %s, since class_attrs contain %s"
                        % (related_attr, class_attr))
                related_class = resolve_module_attr(related_class_name)

                # Create the getter property that uses the class manager to lookup up the related model by id
                def getter(cls):
                    return related_class.objects.get(
                        id=getattr(cls, class_attr))

                return ClassProperty(classmethod(getter))
Ejemplo n.º 21
0
    def dynamic_model_class(self, base_only=False, schema=None, table=None):
        """
            Gets or creates a DbEntity Feature subclass based on the given configuration.
            There are two classes get or created. The base models the imported table.
            The child subclasses the base and adds relations. This way the imported table is not manipulated.
            The child class is returned unless base_only=True is specified
        :params base_only: Default False, indicates that only the base feature class is desired, not the subclass
            that contains the relationships
        :return: The dynamic subclass of the subclass given in feature_class_configuration or None
        """
        if not self.configuration:
            # Return none if no self.configuration exists
            return None

        if not self.configuration.class_attrs or len(
                self.configuration.class_attrs) == 0:
            raise Exception(
                "Class attributes missing from configuration for db_entity %s"
                % self.db_entity.full_name if self.db_entity else self.
                configuration.key)

        if self.configuration.feature_class_owner:
            # If a different DbEntity owners owns the feature_class (e.g. for Result DbEntities), delegate
            self.__class__(
                self.config_entity,
                self.config_entity.computed_db_entities().get(
                    key=self.configuration.feature_class_owner),
                # Same config_entity, ensuring would cause infinite recursion
                no_ensure=True).dynamic_model_class(base_only)

        # Create the base class to represent the "raw" table
        try:
            abstract_feature_class = resolve_module_attr(
                self.configuration.abstract_class_name)
        except Exception, e:
            if not self.configuration:
                logging.exception(
                    "Corrupt DbEntity %s. No feature_class_configuration defined"
                )
            if not self.configuration.abstract_class_name:
                logging.exception(
                    "Corrupt DbEntity %s. No feature_class_configuration.abstract_class_name defined"
                )
            raise
    def create_related_field(self, field_name, related_field_configuration):
        """
            Creates a ForeignKey or ManyToMany field based on related_field_configuration
        :param field_name:
        :param related_field_configuration: A dict containing related_key or related_class_name
            related_key: the instance of the sibling key of the config_entity whose dynamic_model_class is the relation type.
                For DbEntity/Features this would be the db_entity_key and the dynamic_model_class would be its FeatureClass
                For AnalysisModule this would be the analysis module key and its dynamic class
            related_class_name: rather than related_key, any model class, such as BuiltForm, to relate to.
            single: if True this is a ForeignKey (toOne) relationship. Otherwise a ManyToMany is created
        :return: A two-item tuple. First item is the field name and the second is the field.
        """

        config_entity = ConfigEntity._subclassed_by_id(self.configuration.scope)

        # TODO temp coverage of a key name name change
        related_field_configuration['related_key'] = related_field_configuration.get('related_key', related_field_configuration.get('related_db_entity_key'))

        if related_field_configuration.get('related_key', False):
            # field name matches name of peer self.db_entity_key--get it's feature class name
            related_db_entity = get_single_value_or_none(config_entity.computed_db_entities(key=related_field_configuration['related_key']))
            # Always call with no_ensure=True since the config_entity is the same. Otherwise we'd get infinite recursion
            related_class_name_or_model = self.__class__(self.config_entity, related_db_entity, no_ensure=True).dynamic_model_class()
        elif related_field_configuration.get('related_class_name', None):
            # A model class such as BuiltForm
            related_class_name_or_model = resolve_module_attr(related_field_configuration['related_class_name'])
        else:
            raise Exception("No related_key or related_class_name found on feature_class_configuration for self.configuration.key %s" % self.configuration.key)

        logger.info('Creating %r related field for %s using %s', related_field_configuration.get('single', None) and 'single' or 'm2m',
                    field_name, related_field_configuration)
        if related_field_configuration.get('single', None):
            return [field_name,
                    models.ForeignKey(related_class_name_or_model, null=True)]
        else:
            return [field_name,
                    models.ManyToManyField(related_class_name_or_model,
                                           # Pass a custom, readable table name for the through class for ManyToMany relations
                                           db_table='"{schema}"."{table}_{field_name}"'.format(
                                               schema=config_entity.schema(),
                                               table=self.configuration.key,
                                               field_name=field_name))]
    def create_related_field(self, field_name, related_field_configuration):
        """
            Creates a ForeignKey or ManyToMany field based on related_field_configuration
        :param field_name:
        :param related_field_configuration: A dict containing related_key or related_class_name
            related_key: the instance of the sibling key of the config_entity whose dynamic_model_class is the relation type.
                For DbEntity/Features this would be the db_entity_key and the dynamic_model_class would be its FeatureClass
                For AnalysisModule this would be the analysis module key and its dynamic class
            related_class_name: rather than related_key, any model class, such as BuiltForm, to relate to.
            single: if True this is a ForeignKey (toOne) relationship. Otherwise a ManyToMany is created
        :return: A two-item tuple. First item is the field name and the second is the field.
        """

        config_entity = ConfigEntity._subclassed_by_id(self.configuration.scope)

        # TODO temp coverage of a key name name change
        related_field_configuration['related_key'] = related_field_configuration.get('related_key', related_field_configuration.get('related_db_entity_key'))

        if related_field_configuration.get('related_key', False):
            # field name matches name of peer self.db_entity_key--get it's feature class name
            related_db_entity = get_single_value_or_none(config_entity.computed_db_entities(key=related_field_configuration['related_key']))
            # Always call with no_ensure=True since the config_entity is the same. Otherwise we'd get infinite recursion
            related_class_name_or_model = self.__class__(self.config_entity, related_db_entity, no_ensure=True).dynamic_model_class()
        elif related_field_configuration.get('related_class_name', None):
            # A model class such as BuiltForm
            related_class_name_or_model = resolve_module_attr(related_field_configuration['related_class_name'])
        else:
            raise Exception("No related_key or related_class_name found on feature_class_configuration for self.configuration.key %s" % self.configuration.key)

        logger.info('Creating %r related field for %s using %s', related_field_configuration.get('single', None) and 'single' or 'm2m',
                    field_name, related_field_configuration)
        if related_field_configuration.get('single', None):
            return [field_name,
                    models.ForeignKey(related_class_name_or_model, null=True)]
        else:
            return [field_name,
                    models.ManyToManyField(related_class_name_or_model,
                                           # Pass a custom, readable table name for the through class for ManyToMany relations
                                           db_table='"{schema}"."{table}_{field_name}"'.format(
                                               schema=config_entity.schema(),
                                               table=self.configuration.key,
                                               field_name=field_name))]
Ejemplo n.º 24
0
 def feature_class_of_base_class(self, base_feature_class):
     """
         Finds the feature_class of this config_entity that is derived from the given base class
     :param base_feature_class:
     :return:
     """
     db_entities = filter(
         lambda db_entity:
             # Get the configured abstract class
             # The source_db_entity_key is a hack to prevent Result db_entities from being chosen
             issubclass(
                 resolve_module_attr(db_entity.feature_class_configuration.abstract_class_name, object),
                 base_feature_class) and
             not db_entity.source_db_entity_key,
         self.computed_db_entities())
     if len(db_entities) != 1:
         raise Exception(
             "Expected exactly one db_entity matching the base_class {0} but got {1}".format(
                 base_feature_class, db_entities if len(db_entities) > 0 else 'none'))
     return self.db_entity_feature_class(db_entities[0].key)
Ejemplo n.º 25
0
 def feature_class_of_base_class(self, base_feature_class):
     """
         Finds the feature_class of this config_entity that is derived from the given base class
     :param base_feature_class:
     :return:
     """
     db_entities = filter(
         lambda db_entity:
         # Get the configured abstract class
         # The source_db_entity_key is a hack to prevent Result db_entities from being chosen
         issubclass(
             resolve_module_attr(
                 db_entity.feature_class_configuration.abstract_class_name,
                 object), base_feature_class) and not db_entity.
         source_db_entity_key,
         self.computed_db_entities())
     if len(db_entities) != 1:
         raise Exception(
             "Expected exactly one db_entity matching the base_class {0} but got {1}"
             .format(base_feature_class,
                     db_entities if len(db_entities) > 0 else 'none'))
     return self.db_entity_feature_class(db_entities[0].key)
Ejemplo n.º 26
0
 def field_map(path, field_class_path):
     """
         Maps paths to a lambda that converts a value of that path
         to a human readable type. Dates are formatted and
         related objects called object.label
         :param path: The simple or concatinated path to a field
         :param field_class_path: A string representation of the field class
     """
     resolved_field_class = resolve_module_attr(field_class_path)
     if issubclass(resolved_field_class, DateField):
         # Convert to localtime (tzinfo for client will need to be specified in settings or passed from client)
         # We need to convert the timezone offset from [-]HHMM to [-]HH:MM to match javascript's format
         return [path, lambda date: date and re.sub(
             r'(\d{2})(\d{2})$',
             r'\1:\2',
             date.astimezone(tzlocal()).strftime("%Y-%m-%dT%H:%M:%S%z"))
         ]
     if issubclass(resolved_field_class, RelatedField):
         # Insist that the related instances have a label property to present to
         # the user. Use the string format as a last resort
         return [path, FeatureFieldMixin.resolve_instance_label]
     return None
Ejemplo n.º 27
0
 def field_map(path, field_class_path):
     """
         Maps paths to a lambda that converts a value of that path
         to a human readable type. Dates are formatted and
         related objects called object.label
         :param path: The simple or concatinated path to a field
         :param field_class_path: A string representation of the field class
     """
     resolved_field_class = resolve_module_attr(field_class_path)
     if issubclass(resolved_field_class, DateField):
         # Convert to localtime (tzinfo for client will need to be specified in settings or passed from client)
         # We need to convert the timezone offset from [-]HHMM to [-]HH:MM to match javascript's format
         return [
             path, lambda date: date and re.sub(
                 r'(\d{2})(\d{2})$', r'\1:\2',
                 date.astimezone(tzlocal()).strftime("%Y-%m-%dT%H:%M:%S%z"))
         ]
     if issubclass(resolved_field_class, RelatedField):
         # Insist that the related instances have a label property to present to
         # the user. Use the string format as a last resort
         return [path, FeatureFieldMixin.resolve_instance_label]
     return None
Ejemplo n.º 28
0
    def dynamic_model_class(self, base_only=False, schema=None, table=None):
        """
            Gets or creates a DbEntity Feature subclass based on the given configuration.
            There are two classes get or created. The base models the imported table.
            The child subclasses the base and adds relations. This way the imported table is not manipulated.
            The child class is returned unless base_only=True is specified
        :params base_only: Default False, indicates that only the base feature class is desired, not the subclass
            that contains the relationships
        :return: The dynamic subclass of the subclass given in feature_class_configuration or None
        """
        if not self.configuration:
            # Return none if no self.configuration exists
            return None

        if not self.configuration.class_attrs or len(self.configuration.class_attrs) == 0:
            raise Exception("Class attributes missing from configuration for db_entity %s" %
                            self.db_entity.full_name if self.db_entity else self.configuration.key)

        if self.configuration.feature_class_owner:
            # If a different DbEntity owners owns the feature_class (e.g. for Result DbEntities), delegate
            self.__class__(
                self.config_entity,
                self.config_entity.computed_db_entities().get(key=self.configuration.feature_class_owner),
                # Same config_entity, ensuring would cause infinite recursion
                no_ensure=True
            ).dynamic_model_class(base_only)

        # Create the base class to represent the "raw" table
        try:
            abstract_feature_class = resolve_module_attr(self.configuration.abstract_class_name)
        except Exception, e:
            if not self.configuration:
                logging.exception("Corrupt DbEntity %s. No feature_class_configuration defined")
            if not self.configuration.abstract_class_name:
                logging.exception("Corrupt DbEntity %s. No feature_class_configuration.abstract_class_name defined")
            raise
Ejemplo n.º 29
0
def disable_signal_handler_for_celery(signal_ref_path, handler_path, uid, limit_to_classes_paths):
    for cls in filter_classes(limit_to_classes_paths):
        resolve_module_attr(signal_ref_path).disconnect(resolve_module_attr(handler_path), resolve_module_attr(cls), True, uid)
Ejemplo n.º 30
0
    # Also use first=False to indicate recursion so we don't resend the start signal to the client
    updated_kwargs = merge(
        remove_keys(kwargs, ['signal_path', 'current_job', 'bundle']),
        dict(instance=bundle.instances, user_id=bundle.user_id, recurse=True, current_job=job))
    logger.warn("kwargs %s" % updated_kwargs)

    logger.info("Running handlers for signal {signal_path} for {bundle}".format(
        config_entity=config_entity,
        username=user.username,
        signal_path=publishing_info['signal_path'],
        bundle=unicode(bundle)))

    # Send the signal. The listening publishers will run in sequence
    # We always send the signal in the context of the underlying config_entity class if one exists
    resolve_module_attr(publishing_info['signal_path']).send(
        sender=config_entity.__class__ if config_entity else bundle.clazz, **updated_kwargs
    )
    try:
        # Make sure no transactions are outstanding
        # This shouldn't be needed once Django is upgraded
        transaction.commit()
    except Exception, e:
        pass

    event = 'postSavePublisherProportionCompleted'
    logger.info("Sending message %s for signal complete %s to user %s with %s, and proportion %s" %
                 (event, publishing_info['signal_proportion_lookup_name'], user.username, unicode(bundle), publishing_info['proportion']))

    if kwargs.get('update_setup_percent_complete'):
        for instance in bundle.client_instances:
            # This creates an update statement to increment to the setup_percent_complete field
Ejemplo n.º 31
0
             user_id=bundle.user_id,
             recurse=True,
             current_job=job))
    logger.warn("kwargs %s" % updated_kwargs)

    logger.info(
        "Running handlers for signal {signal_path} for {bundle}".format(
            config_entity=config_entity,
            username=user.username,
            signal_path=publishing_info['signal_path'],
            bundle=unicode(bundle)))

    # Send the signal. The listening publishers will run in sequence
    # We always send the signal in the context of the underlying config_entity class if one exists
    resolve_module_attr(publishing_info['signal_path']).send(
        sender=config_entity.__class__ if config_entity else bundle.clazz,
        **updated_kwargs)
    try:
        # Make sure no transactions are outstanding
        # This shouldn't be needed once Django is upgraded
        transaction.commit()
    except Exception, e:
        pass

    event = 'postSavePublisherProportionCompleted'
    logger.info(
        "Sending message %s for signal complete %s to user %s with %s, and proportion %s"
        % (event, publishing_info['signal_proportion_lookup_name'],
           user.username, unicode(bundle), publishing_info['proportion']))

    if kwargs.get('update_setup_percent_complete'):
Ejemplo n.º 32
0
class DbEntities(models.Model):

    db_entities = models.ManyToManyField('DbEntity',
                                         through='DbEntityInterest')

    class Meta:
        abstract = True

    def add_db_entity_interests(self, *db_entity_interests):
        """
            Adds one or more unsaved DbEntityInterests to the instance's collection.
            If the instance has not yet overridden its parents' db_entities by adding at least one DbEntityInterest,
            the parents DbEntityInterests will be adopted and then the db_entities give here will be added
        :return:
        """
        # This check exists to avoid infinite recursion, since db_entities are sometimes added post_config_entity_save handler
        if len(db_entity_interests) > 0:
            # Even though the field name is db_entities, we need to pass the DbEntityInterests
            self._add('db_entities', *db_entity_interests)

    def remove_db_entity_interests(self, *db_entity_interests):
        self._remove('db_entities', *db_entity_interests)

    def feature_class_of_base_class(self, base_feature_class):
        """
            Finds the feature_class of this config_entity that is derived from the given base class
        :param base_feature_class:
        :return:
        """
        db_entities = filter(
            lambda db_entity:
            # Get the configured abstract class
            # The source_db_entity_key is a hack to prevent Result db_entities from being chosen
            issubclass(
                resolve_module_attr(
                    db_entity.feature_class_configuration.abstract_class_name,
                    object), base_feature_class) and not db_entity.
            source_db_entity_key,
            self.computed_db_entities())
        if len(db_entities) != 1:
            raise Exception(
                "Expected exactly one db_entity matching the base_class {0} but got {1}"
                .format(base_feature_class,
                        db_entities if len(db_entities) > 0 else 'none'))
        return self.db_entity_feature_class(db_entities[0].key)

    def db_entity_feature_class(self,
                                key,
                                abstract_class=False,
                                base_feature_class=False):
        """
            Resolves the Feature class subclass of this config_entity for the given key, or the base class version
            if base_class-True. Note that this is slightly different than asking a DbEntity for its corresponding
            Feature class. A DbEntity will always use its owning ConfigEntity for the config_entity property
            of the Feature class. This method uses self as the ConfigEntity, which is useful when self
            is a lower ConfigEntity in the hierarchy (such as a Scenario) and the Feature class needs
            access to related Feature classes that are also at a low level. This works because a lower ConfigEntity
            has access to DbEntities that the higher one does not, which allows queries between from a higher
            DbEntity to join a lower one.
        :param key: The DbEntity key
        :param abstract_class, Default False, if True returns the abstract base class instead of the subclass
        :param base_feature_class, Default False, if True returns the base class instead of the default rel class
        :return:
        """
        try:
            original_db_entity = self.computed_db_entities().get(key=key)
        except Exception, e:
            raise Exception(
                "The DbEntity key %s could not be found in the computed DbEntities of the ConfigEntity %s, which contains %s"
                % (key, self.name, ', '.join(
                    map(lambda db_entity: db_entity.key,
                        self.computed_db_entities()))))

        source_db_entity_key = original_db_entity.source_db_entity_key if original_db_entity.source_db_entity_key else key
        db_entity = self.computed_db_entities().get(key=source_db_entity_key)
        # Resolve the source_db_entity_key of the DbEntity or just use key
        from footprint.main.models.feature.feature_class_creator import FeatureClassCreator
        if abstract_class:
            subclass = resolve_module_attr(
                db_entity.feature_class_configuration.abstract_class_name)
        elif base_feature_class or db_entity.feature_class_configuration.no_table_associations:
            subclass = FeatureClassCreator(
                self, db_entity).dynamic_model_class(base_only=True)
        else:
            subclass = FeatureClassCreator(self,
                                           db_entity).dynamic_model_class()
        if not subclass:
            raise Exception(
                "For config_entity {0} no class associated with db_entity_key {1}{2}. "
                "Register a table in default_db_entities() of the owning config_entity."
                .format(
                    unicode(self), key,
                    ", even after resolving source_db_entity_key to {0}".
                    format(source_db_entity_key)
                    if source_db_entity_key != key else ''))
        return subclass