def get_form_class(self): """ Returns the form class for this Feature Class. """ try: klass = get_class(self.form) except Exception, e: raise (FeatureConfigurationError( "Feature class %s is not configured with a valid form class. \ Could not import %s.\n%s" % (self._model.__name__, self.form, e)), None, sys.exc_info()[2])
def get_form_class(self): """ Returns the form class for this Feature Class. """ try: klass = get_class(self.form) except Exception as e: raise FeatureConfigurationError( "Feature class %s is not configured with a valid form class. \ Could not import %s.\n%s" % (self._model.__name__, self.form, e)) if not issubclass(klass, FeatureForm): raise FeatureConfigurationError( "Feature class %s's form is not a subclass of \ features.forms.FeatureForm." % (self._model.__name__, )) return klass
def get_valid_children(self): if not self.valid_children: raise FeatureConfigurationError( "%r is not a properly configured FeatureCollection" % (self._model)) valid_child_classes = [] for vc in self.valid_children: try: vc_class = get_class(vc) except: raise FeatureConfigurationError( "Error trying to import module %s" % vc) from features.models import Feature if not issubclass(vc_class, Feature): raise FeatureConfigurationError( "%r is not a Feature; can't be a child" % vc) valid_child_classes.append(vc_class) return valid_child_classes
def testNoDots(self): with self.assertRaises(ValueError): get_class('hello')
def testExistent(self): cls = get_class('ConfigParser.RawConfigParser') import ConfigParser self.assertEqual(cls, ConfigParser.RawConfigParser, 'Got the wrong class')
def testNonExistent(self): with self.assertRaises(AttributeError): get_class('sys.poodoo')
def __init__(self, model): # Import down here to avoid circular reference from features.models import Feature, FeatureCollection # call this here to ensure that permsissions get created #enable_sharing() if not issubclass(model, Feature): raise FeatureConfigurationError('Is not a subclass of \ features.models.Feature') self._model = model name = model.__name__ if not getattr(model, 'Options', False): raise FeatureConfigurationError( 'Have not defined Options inner-class on registered feature \ class %s' % (name, )) self._options = model.Options if not hasattr(self._options, 'form'): raise FeatureConfigurationError( "Feature class %s is not configured with a form class. \ To specify, add a `form` property to its Options inner-class." % (name, )) if not isinstance(self._options.form, str): raise FeatureConfigurationError( "Feature class %s is configured with a form property that is \ not a string path." % (name, )) self.form = self._options.form """ Path to FeatureForm used to edit this class. """ self.slug = slugify(name) """ Name used in the url path to this feature as well as part of the Feature's uid """ self.verbose_name = getattr(self._options, 'verbose_name', name) """ Name specified or derived from the feature class name used in the user interface for representing this feature class. """ self.form_template = getattr(self._options, 'form_template', 'features/form.html') """ Location of the template that should be used to render forms when editing or creating new instances of this feature class. """ self.form_context = getattr(self._options, 'form_context', {}) """ Context to merge with default context items when rendering templates to create or modify features of this class. """ self.show_context = getattr(self._options, 'show_context', {}) """ Context to merge with default context items when rendering templates to view information about instances of this feature class. """ self.icon_url = getattr(self._options, 'icon_url', None) """ Optional; URL to 16x16 icon to use in kmltree Use full URL or relative to MEDIA_URL """ self.links = [] """ Links associated with this class. """ opts_links = getattr(self._options, 'links', False) if opts_links: self.links.extend(opts_links) self.enable_copy = getattr(self._options, 'disable_copy', True) """ Enable copying features. Uses the feature class' copy() method. Defaults to True. """ # Add a copy method unless disabled if self.enable_copy: self.links.insert( 0, edit('Copy', 'features.views.copy', select='multiple single', edits_original=False)) confirm = "Are you sure you want to delete this feature and it's contents?" # Add a multi-share generic link # TODO when the share_form view takes multiple instances # we can make sharing a generic link #self.links.insert(0, edit('Share', # 'features.views.share_form', # select='multiple single', # method='POST', # edits_original=True, #)) # Add a multi-delete generic link self.links.insert( 0, edit( 'Delete', 'features.views.multi_delete', select='multiple single', method='DELETE', edits_original=True, confirm=confirm, )) # Add a staticmap generic link # export_png = getattr(self._options, 'export_png', True) # RDH - the above code will always trigger unless mp-drawing module explicitly set to False. if getattr(self._options, 'export_png', False): # TODO: Bring in the staticmap module logger.warning("Uncomment the following code") # self.links.insert(0, alternate('PNG Image', # 'staticmap.views.staticmap_link', # select='multiple single', # method='GET', # )) # Add a geojson generic link export_geojson = getattr(self._options, 'export_geojson', True) if export_geojson: self.links.insert( 0, alternate( 'GeoJSON', 'features.views.geojson_link', select='multiple single', method='GET', )) self.valid_children = getattr(self._options, 'valid_children', None) """ valid child classes for the feature container """ if self.valid_children and not issubclass(self._model, FeatureCollection): raise FeatureConfigurationError("valid_children Option only \ for FeatureCollection classes") self.manipulators = [] """ Required manipulators applied to user input geometries """ manipulators = getattr(self._options, 'manipulators', []) for m in manipulators: try: manip = get_class(m) except Exception as e: # Don't lose the original exception, fake a PEP3134 exception # chain (too bad we're not Py3k) t, v, tb = sys.exc_info() s = "Error trying to import module %s" % m raise FeatureConfigurationError(s, t, v, tb) # Test that manipulator is compatible with this Feature Class geom_field = self._model.geometry_final._field.__class__.__name__ if geom_field not in manip.Options.supported_geom_fields: raise FeatureConfigurationError( "%s does not support %s geometry types (only %r)" % (m, geom_field, manip.Options.supported_geom_fields)) #logger.debug("Added required manipulator %s" % m) self.manipulators.append(manip) self.optional_manipulators = [] """ Optional manipulators that may be applied to user input geometries """ optional_manipulators = getattr(self._options, 'optional_manipulators', []) for m in optional_manipulators: try: manip = get_class(m) except: raise FeatureConfigurationError( "Error trying to import module %s" % m) # Test that manipulator is compatible with this Feature Class geom_field = self._model.geometry_final._field.__class__.__name__ try: if geom_field not in manip.Options.supported_geom_fields: raise FeatureConfigurationError( "%s does not support %s geometry types (only %r)" % (m, geom_field, manip.Options.supported_geom_fields)) except AttributeError: raise FeatureConfigurationError( "%s is not set up properly; must have " "Options.supported_geom_fields list." % m) #logger.debug("Added optional manipulator %s" % m) self.optional_manipulators.append(manip) self.enable_kml = True """ Enable kml visualization of features. Defaults to True. """ # Add a kml link by default if self.enable_kml: self.links.insert( 0, alternate('KML', 'features.views.kml', select='multiple single')) self.links.insert( 0, alternate('KMZ', 'features.views.kmz', select='multiple single')) for link in self.links: if self._model not in link.models: link.models.append(self._model)
def __init__(self, rel, title, view, method='GET', select='single', type=None, slug=None, generic=False, models=None, extra_kwargs={}, confirm=False, edits_original=None, must_own=False, limit_to_groups=None): self.rel = rel """Type of link - alternate, related, edit, or edit_form. """ try: self.view = get_class(view) """ View function handling requests to this link. """ except Exception as err: msg = 'Link "%s" configured with invalid path to view %s' % (title, view) msg += '\n%s\n' % str(err) if "cannot import" in str(err): msg += "(Possible cause: importing Features at the top level in views.py can cause" msg += " circular dependencies; Try to import Features within the view function)" raise FeatureConfigurationError(msg) self.title = title """ Human-readable title for the link to be shown in the user interface. """ self.method = method """ For rel=edit links, identifies whether a form should be requested or that url should just be POST'ed to. """ self.type = type """ MIME type of this link, useful for alternate links. May in the future be used to automatically assign an icon in the dropdown Export menu. """ self.slug = slug """ Part of this link's path. """ self.select = select """ Determines whether this link accepts requests with single or multiple instances of a feature class. Valid values are "single", "multiple", "single multiple", and "multiple single". """ self.extra_kwargs = extra_kwargs """ Extra keyword arguments to pass to the view. """ self.generic = generic """ Whether this view can be applied to multiple feature classes. """ self.models = models """ List of feature classes that a this view can be applied to, if it is generic. """ self.confirm = confirm """ Confirmation message to show the user before POSTing to rel=edit link """ self.edits_original = edits_original """ Set to false for editing links that create a copy of the original. This will allow users who do not own the instance(s) but can view them perform the action. """ self.must_own = must_own if self.edits_original: self.must_own = True """ Whether this link should be accessible to non-owners. Default link behavior is False; i.e. Link can be used for shared features as well as for user-owned features. If edits_original is true, this implies must_own = True as well. """ self.limit_to_groups = limit_to_groups """ Allows you to specify groups (a list of group names) that should have access to the link. Default is None; i.e. All users have link access regardless of group membership """ if self.models is None: self.models = [] # Make sure title isn't empty if self.title is '': raise FeatureConfigurationError('Link title is empty') valid_options = ('single', 'multiple', 'single multiple', 'multiple single') # Check for valid 'select' kwarg if self.select not in valid_options: raise FeatureConfigurationError( 'Link specified with invalid select option "%s"' % (self.select, )) # Create slug from the title unless a custom slug is specified if self.slug is None: self.slug = slugify(title) # Make sure the view has the right signature self._validate_view(self.view)
def __init__(self, model): # Import down here to avoid circular reference from features.models import Feature, FeatureCollection # call this here to ensure that permsissions get created #enable_sharing() if not issubclass(model, Feature): raise FeatureConfigurationError('Is not a subclass of \ features.models.Feature') self._model = model name = model.__name__ if not getattr(model, 'Options', False): raise FeatureConfigurationError( 'Have not defined Options inner-class on registered feature \ class %s' % (name, )) self._options = model.Options if not hasattr(self._options, 'form'): raise FeatureConfigurationError( "Feature class %s is not configured with a form class. \ To specify, add a `form` property to its Options inner-class." % (name,)) if not isinstance(self._options.form, str): raise FeatureConfigurationError( "Feature class %s is configured with a form property that is \ not a string path." % (name,)) self.form = self._options.form """ Path to FeatureForm used to edit this class. """ self.slug = slugify(name) """ Name used in the url path to this feature as well as part of the Feature's uid """ self.verbose_name = getattr(self._options, 'verbose_name', name) """ Name specified or derived from the feature class name used in the user interface for representing this feature class. """ self.form_template = getattr(self._options, 'form_template', 'features/form.html') """ Location of the template that should be used to render forms when editing or creating new instances of this feature class. """ self.form_context = getattr(self._options, 'form_context', {}) """ Context to merge with default context items when rendering templates to create or modify features of this class. """ self.show_context = getattr(self._options, 'show_context', {}) """ Context to merge with default context items when rendering templates to view information about instances of this feature class. """ self.icon_url = getattr(self._options, 'icon_url', None) """ Optional; URL to 16x16 icon to use in kmltree Use full URL or relative to MEDIA_URL """ self.links = [] """ Links associated with this class. """ opts_links = getattr(self._options, 'links', False) if opts_links: self.links.extend(opts_links) self.enable_copy = getattr(self._options, 'disable_copy', True) """ Enable copying features. Uses the feature class' copy() method. Defaults to True. """ # Add a copy method unless disabled if self.enable_copy: self.links.insert(0, edit('Copy', 'features.views.copy', select='multiple single', edits_original=False)) confirm = "Are you sure you want to delete this feature and it's contents?" # Add a multi-share generic link # TODO when the share_form view takes multiple instances # we can make sharing a generic link #self.links.insert(0, edit('Share', # 'features.views.share_form', # select='multiple single', # method='POST', # edits_original=True, #)) # Add a multi-delete generic link self.links.insert(0, edit('Delete', 'features.views.multi_delete', select='multiple single', method='DELETE', edits_original=True, confirm=confirm, )) # Add a staticmap generic link export_png = getattr(self._options, 'export_png', True) if export_png: # TODO: Bring in the staticmap module logger.warning("Uncomment the following code") # self.links.insert(0, alternate('PNG Image', # 'staticmap.views.staticmap_link', # select='multiple single', # method='GET', # )) # Add a geojson generic link export_geojson = getattr(self._options, 'export_geojson', True) if export_geojson: self.links.insert(0, alternate('GeoJSON', 'features.views.geojson_link', select='multiple single', method='GET', )) self.valid_children = getattr(self._options, 'valid_children', None) """ valid child classes for the feature container """ if self.valid_children and not issubclass(self._model, FeatureCollection): raise FeatureConfigurationError("valid_children Option only \ for FeatureCollection classes") self.manipulators = [] """ Required manipulators applied to user input geometries """ manipulators = getattr(self._options, 'manipulators', []) for m in manipulators: try: manip = get_class(m) except Exception, e: # Don't lose the original exception, fake a PEP3134 exception # chain (too bad we're not Py3k) t, v, tb = sys.exc_info() s = "Error trying to import module %s" % m raise FeatureConfigurationError, (s, t, v), tb # Test that manipulator is compatible with this Feature Class geom_field = self._model.geometry_final._field.__class__.__name__ if geom_field not in manip.Options.supported_geom_fields: raise FeatureConfigurationError("%s does not support %s geometry types (only %r)" % (m, geom_field, manip.Options.supported_geom_fields)) #logger.debug("Added required manipulator %s" % m) self.manipulators.append(manip)
def __init__(self, rel, title, view, method='GET', select='single', type=None, slug=None, generic=False, models=None, extra_kwargs={}, confirm=False, edits_original=None, must_own=False, limit_to_groups=None): self.rel = rel """Type of link - alternate, related, edit, or edit_form. """ try: self.view = get_class(view) """ View function handling requests to this link. """ except Exception as err: msg = 'Link "%s" configured with invalid path to view %s' % (title, view) msg += '\n%s\n' % str(err) if "cannot import" in str(err): msg += "(Possible cause: importing Features at the top level in views.py can cause" msg += " circular dependencies; Try to import Features within the view function)" raise FeatureConfigurationError(msg) self.title = title """ Human-readable title for the link to be shown in the user interface. """ self.method = method """ For rel=edit links, identifies whether a form should be requested or that url should just be POST'ed to. """ self.type = type """ MIME type of this link, useful for alternate links. May in the future be used to automatically assign an icon in the dropdown Export menu. """ self.slug = slug """ Part of this link's path. """ self.select = select """ Determines whether this link accepts requests with single or multiple instances of a feature class. Valid values are "single", "multiple", "single multiple", and "multiple single". """ self.extra_kwargs = extra_kwargs """ Extra keyword arguments to pass to the view. """ self.generic = generic """ Whether this view can be applied to multiple feature classes. """ self.models = models """ List of feature classes that a this view can be applied to, if it is generic. """ self.confirm = confirm """ Confirmation message to show the user before POSTing to rel=edit link """ self.edits_original = edits_original """ Set to false for editing links that create a copy of the original. This will allow users who do not own the instance(s) but can view them perform the action. """ self.must_own = must_own if self.edits_original: self.must_own = True """ Whether this link should be accessible to non-owners. Default link behavior is False; i.e. Link can be used for shared features as well as for user-owned features. If edits_original is true, this implies must_own = True as well. """ self.limit_to_groups = limit_to_groups """ Allows you to specify groups (a list of group names) that should have access to the link. Default is None; i.e. All users have link access regardless of group membership """ if self.models is None: self.models = [] # Make sure title isn't empty if self.title is '': raise FeatureConfigurationError('Link title is empty') valid_options = ('single', 'multiple', 'single multiple', 'multiple single') # Check for valid 'select' kwarg if self.select not in valid_options: raise FeatureConfigurationError( 'Link specified with invalid select option "%s"' % ( self.select, )) # Create slug from the title unless a custom slug is specified if self.slug is None: self.slug = slugify(title) # Make sure the view has the right signature self._validate_view(self.view)