def wrapper(request, *args, **kwargs): # Dynamic resources based on a ConfigEntity instance need to pass the config_entity__id so that we can properly construct the dynamic resource if hasattr(self.Meta, 'abstract') and self.Meta.abstract: wrapped_view = self.subclass_resource_if_needed(view, request) # During the subclassing process we may have removed parameters needed only to resolve # the dynamic Resource class. We don't want those parameters to be used by the dynamic # resource for filtering filter = getattr(request, '_filters', request.GET) # request.GET is used for filtering along with kwargs['GET']. They are combined (see get_object_list) encoding = request.GET.encoding request._original_params = request.GET.copy() # Make sure the values are singular so we don't # get weird array values back request.GET = http.QueryDict( '&'.join( map_dict(lambda key, value: '%s=%s' % (key, value[-1] if isinstance(value, list) else value), filter) ), encoding=encoding ) else: wrapped_view = super(ModelResource, self).wrap_view(view) return wrapped_view(request, *args, **kwargs)
def dehydrate(self, bundle): bundle = super(JoinFeatureResource, self).dehydrate(bundle) def map_related_to_field_and_label(field_name, related_model): """ Creates a mapping from the _id attribute of ForeignKeys to to the related attribute (e.g. built_form_id to built_form). It puts the related attribute in the main dict with its resource_uri and puts a label representation in the __labels dict so that the related attribute can map from its id to a label. This is similar to what the regular FeatureResource does but it is more manual since we only have the _id attribute and must resolve the related instance ourselves """ client_field_name = self.client_field_name(field_name) related_id = getattr(bundle.obj, client_field_name) if hasattr(bundle.obj, client_field_name) else None if related_id: related_model_instance = related_model.objects.get(id=related_id) related_field_name = field_name.replace('_id', '') return { # Get the resource class of the related model so we can make a uri related_field_name: FootprintResource.resolve_resource_class(related_model)().get_resource_uri(related_model_instance), # The label representation '__labels': {related_field_name: LayerSelection.resolve_instance_label(related_model_instance)} } return None related_field_lookup = self.related_field_lookup if related_field_lookup: # For join queries, we need to manually add related models--not the joins, # rather related models on the main feature, such as a BuiltForm reference bundle.data = deep_merge(bundle.data, *compact(map_dict( map_related_to_field_and_label, related_field_lookup ))) return bundle
def update_summary_results(self, query_result): """ Updates the summary results with the given QuerySet or results list :param self: :param query_result: :return: """ if isinstance(query_result, QuerySet): # Find aggregate and normal field names aggregate_names = query_result.aggregate_names if hasattr(query_result, 'aggregate_names') else [] self.summary_fields = (query_result.field_names if hasattr(query_result, 'field_names') else []) + aggregate_names # Find aggregate and normal field titles aggregate_titles = map_dict(lambda key, value: self.cleanup_title(key), query_result.query.aggregates) if hasattr(query_result.query, 'aggregates') else [] titles = map(lambda tup: self.cleanup_title(tup[1]), query_result.query.select) + aggregate_titles # Create a lookup from field name to title self.summary_field_title_lookup = dual_map_to_dict(lambda key, value: [key, value], self.summary_fields, titles) self.summary_query_sql = str(query_result.query) elif len(query_result) > 0: # For single row aggregates. TODO figure out who to extract the names from the query self.summary_fields = query_result[0].keys() self.summary_field_title_lookup = map_to_dict(lambda key: [key, key], self.summary_fields) self.summary_query_sql = str(query_result.query) self.summary_results = list(query_result) self.save()
def get(self, resource_name, ids=None, user=None, query_params=None, **kwargs): """ Get all or the instances specified by ids :param resource_name: :param ids: :param user: :return: """ if query_params: # The test framework takes care of the username unless query_params are specified, # apparently query_params.update(username=user.username) return self.api_client.get( '%s/%s/%s%s' % (self.base_uri, resource_name, ('set/%s' % ';'.join(ids)) if ids else '', ('?%s' % '&'.join( map_dict(lambda k, v: '%s=%s' % (k, v), query_params))) if query_params else ''), format='json', authentication=self.get_credentials(user), **kwargs)
def parse_queryset_inner_argument(self, argument): """ The inner arguments of the queryset command are either a simple column/alias path or a dict in the case of aggregate functions, e.g. dict(Avg='book__rating') to indicate Avg('book__rating') :param argument: :return: """ if isinstance(argument, dict): return map_dict(lambda key, value: self.resolve_models_class(key)(value), argument)[0] return argument
def wrapper(*args, **kwargs): """ Wraps the function to include the logging. Logging includes the args and kwargs in the log message :param args: :param kwargs: :return: """ args_str = '\n\targs %s' % ', '.join(map(lambda arg: '\n\t\t%s' % unicode(arg), args)) if args else '' kwargs_str = '\n\tkwargs %s' % ', '.join(map_dict(lambda key, value: '\n\t\t%s' % '='.join([unicode(key), unicode(value)]), kwargs)) if kwargs else '' log.log(level, 'Calling %s%s%s' % (logmsg, args_str, kwargs_str)) return func(*args, **kwargs)
def parse_queryset_inner_argument(self, argument): """ The inner arguments of the queryset command are either a simple column/alias path or a dict in the case of aggregate functions, e.g. dict(Avg='book__rating') to indicate Avg('book__rating') :param argument: :return: """ if isinstance(argument, dict): return map_dict( lambda key, value: self.resolve_models_class(key)(value), argument)[0] return argument
def default_remote_db_entities(self): # The Behavior keyspace behavior_key = BehaviorKey.Fab.ricate # Used to load Behaviors defined elsewhere get_behavior = lambda key: Behavior.objects.get(key=behavior_key(key)) mapbox_layers = { 'Aerial Photo': { "id": "elevan.kib62d92", "key": "mapbox_aerial" }, 'Simple Streets': { "id": "elevan.e53fa071", "key": "mapbox_streets" } } mapbox_base_url = "https://api.mapbox.com/v4/{id}/{{z}}/{{x}}/{{y}}.png?access_token={api_key}" mapbox_setups = map_dict( lambda name, attrs: DbEntity( key="mapbox_" + attrs['key'], name="Mapbox: " + name, url=mapbox_base_url.format(id=attrs['id'], api_key=settings.MAPBOX_API_KEY), hosts=["a", "b", "c", "d"], schema=self.config_entity, no_feature_class_configuration=True, feature_behavior=FeatureBehavior(behavior=get_behavior( 'remote_imagery')), _categories=[ Category(key=DbEntityCategoryKey.KEY_CLASSIFICATION, value=DbEntityCategoryKey.BASEMAPS) ]), mapbox_layers) cloudmade_setups = [ DbEntity(key='osm_default', name='OpenStreetMap: Base Layer', url="http://a.tile.openstreetmap.org/{z}/{x}/{y}.png", schema=self.config_entity, no_feature_class_configuration=True, feature_behavior=FeatureBehavior( behavior=get_behavior('remote_imagery')), _categories=[ Category(key=DbEntityCategoryKey.KEY_CLASSIFICATION, value=DbEntityCategoryKey.BASEMAPS) ]) ] return mapbox_setups + cloudmade_setups
def build_style_string(style, separator=' '): """ Creates key value pairs as a string for the given Style with the given separator between the paris :param style: :param separator: Default ' ' :return: """ style_string = separator.join( map_dict( lambda key, value: '{key}: {value};'.format(key=dasherize(key), value=map_value(value)), compact_dict(style))) return style_string
def create_layer_style_for_related_field(related_field_path, related_model, color_lookup, color_lookup_field, visible): """ Creates the CSS context_dict to use in a LayerStyle instance for the given ForeignKey model field :param related_field_path: If the field is a many-to-many, specify this, e.g. 'built_form__id'. :param related_model - Model object having items with a color_lookup_field :param color_lookup: A dict that maps a field of the ForeignKey model class to a color :param color_lookup_field: The field of the ForeignKey model that matches the keys of the color_lookup :return: A complete default context_dict for the give model field """ def create_style_value_context(lookup_field_value, color): if color: try: foreign_key = related_model.objects.get(**{color_lookup_field: lookup_field_value}).id return StyleValueContext( value=foreign_key, symbol='=', style=Style( polygon_fill=color, polygon_opacity=0.8, line_color='#d2d2d5', line_opacity=0.7, line_width=.5 ) ) except: return None return None return dict( geometry_type=GeometryTypeKey.POLYGON, style_attributes=[ dict( attribute=related_field_path, opacity=1, visible=visible, style_type=StyleTypeKey.CATEGORICAL, style_value_contexts=[null_style_value_context()] + sorted(compact(map_dict( create_style_value_context, color_lookup )), key=lambda style_value_context: style_value_context.value) ) ] )
def wrapper(*args, **kwargs): """ Wraps the function to include the logging. Logging includes the args and kwargs in the log message :param args: :param kwargs: :return: """ args_str = '\n\targs %s' % ', '.join( map(lambda arg: '\n\t\t%s' % unicode(arg), args)) if args else '' kwargs_str = '\n\tkwargs %s' % ', '.join( map_dict( lambda key, value: '\n\t\t%s' % '='.join( [unicode(key), unicode(value)]), kwargs)) if kwargs else '' log.log(level, 'Calling %s%s%s' % (logmsg, args_str, kwargs_str)) return func(*args, **kwargs)
def get(self, resource_name, ids=None, user=None, query_params=None, **kwargs): """ Get all or the instances specified by ids :param resource_name: :param ids: :param user: :return: """ if query_params: # The test framework takes care of the username unless query_params are specified, # apparently query_params.update(username=user.username) return self.api_client.get( '%s/%s/%s%s' % ( self.base_uri, resource_name, ('set/%s' % ';'.join(ids)) if ids else '', ('?%s' % '&'.join(map_dict(lambda k, v: '%s=%s' % (k,v), query_params))) if query_params else ''), format='json', authentication=self.get_credentials(user), **kwargs)
def resolve_resource_class(cls, model_class, related_descriptors=None, queryset=None, base_resource_class=None, object_class=None, no_cache=False, use_version_fields=False, only_abstract_resources=True, additional_fields_dict=None, is_join_query=False, limit_fields=None, for_template=False): """ Match the model_class to an existing resource class by iterating through subclasses of self.__class__ If no match occurs generate one if db_entity is specified, else None :param model_class: :param related_descriptors: Optionally provided a dictionary of related descriptors of the model_class to use to create related resource fields in the case of dynamic resource creation. If unspecified, related_descriptors are found using the base resource class's resolve_related_descriptors method :param no_cache: Don't use cached resources. Specifying queryset also prevents the cache being consulted :param use_version_fields: Special case for versioned models. Instructs the resource class creation to create dynamic fields to look in the model_class's Revisionable._version_field_dict to resolve the value of a field before looking at the database (current object) version of the field. :param additional_fields_dict. Optional dict that gives the resource class additional fields with values. This is not for related fields, rather special cases like passing the layer_selection to the FeatureResource. :param for_template: Currently just used for Features, but this indicates a template version of the resource is being sought :param is_join_query: True if the is a join query table or similar. This means that the queryset isn't actually valid yet :return: The matching or created resource """ # Never seek a cached resource if a queryset or no_cache is specified allow_caching = queryset is None and not no_cache cls.logger.info("Resolving Resource Class for model_class %s with query %s and allow_caching %s and only_abstract_resources %s and base_resource_class %s", model_class.__name__, queryset, allow_caching, only_abstract_resources, base_resource_class) if not base_resource_class and allow_caching: # Try to get a cached resource if a base_resource_class is present and we allow caching # of this resource. We never allow caching if the queryset changes from request to request # Instead we subclass the best matching existing resource class resources = FootprintResource.match_existing_resources(model_class) if len(resources) > 1: logging.warn("Too many resources for model class: %s. Resources; %s" % (model_class, resources)) if (len(resources) > 0): cls.logger.info("Found existing resource class %s for model class %s" % (resources[0], model_class)) # Done, return the existing resource return resources[0] # If we don't allow caching or no resource was found if only_abstract_resources: # See if we can match an abstract resource to be our base. # We look for the best matching resource who's model class matches or is a superclass or our model class. base_resource_classes = FootprintResource.match_existing_resources(model_class, only_abstract_resources=True) cls.logger.info("Matching abstract base resource classes: %s" % base_resource_classes) base_resource_class = base_resource_classes[0] if len(base_resource_classes)==1 else base_resource_class else: # Fist try to find a concrete one that we already created concrete_base_resource_classes = FootprintResource.match_existing_resources(model_class, allow_abstract_resources=False) if len(concrete_base_resource_classes)==1: # Found a concrete resource class that was already created for this model # If this isn't a join query we can return this resource class base_resource_class = concrete_base_resource_classes[0] if not is_join_query: return base_resource_class else: # Try to find an abstract one all_base_resource_classes = FootprintResource.match_existing_resources(model_class, only_abstract_resources=True) cls.logger.info("Matching abstract base resource classes: %s" % all_base_resource_classes) base_resource_class = all_base_resource_classes[0] if len(all_base_resource_classes)==1 else base_resource_class if is_join_query: # Join queries must pass in the limited fields from the layer_selection.result_map.result_fields limit_api_fields = limit_fields else: # Non-join queries find their fields from the model_class limit_api_fields = limited_api_fields(model_class, for_template=for_template) resource_class = base_resource_class or cls if base_resource_class: cls.logger.info("Resolved base_resource_class: %s" % base_resource_class) else: cls.logger.info("Could not resolve a base_resource_class. Using resource class %s" % cls) related_descriptors = related_descriptors or \ resource_class.resolve_related_descriptors(model_class) or \ {} cls.logger.info("Creating a resource subclass of %s for model_class %s with the following related descriptors %s" % ( resource_class, model_class, ', '.join(map_dict( lambda related_field_name, related_descriptor: related_field_name, related_descriptors )))) related_fields = merge(additional_fields_dict or {}, map_dict_to_dict( lambda related_field_name, relationship_dict: cls.related_resource_field( related_field_name, merge( dict( # This is passed in, but might be overriden in the relationship_dict use_version_fields=use_version_fields ), # Already a relationship dict relationship_dict if isinstance(relationship_dict, dict) else dict(), # Not a relationship dict, just a descriptor, wrap it dict(descriptor=relationship_dict) if not isinstance(relationship_dict, dict) else dict(), ), # Always allow cached related resource classes. # I think this is always safe no_cache=False), related_descriptors)) meta = merge( dict(fields=limit_api_fields) if limit_api_fields else dict(excludes=(resource_class.Meta.excludes if hasattr( resource_class.Meta, 'excludes') else []) + cls.dynamic_excludes( model_class)), dict(queryset=queryset, object_class=object_class) if queryset else dict()) return get_dynamic_resource_class( resource_class, model_class, # Created related fields from the related_descriptors fields=related_fields, # Optionally set the fields and queryset on the Meta class meta_fields=meta )