def update_map_feature(request_dict, user, feature): """ Update a map feature. Expects JSON in the request body to be: {'model.field', ...} Where model is either 'tree', 'plot', or another map feature type and field is any field on the model. UDF fields should be prefixed with 'udf:'. This method can be used to create a new map feature by passing in an empty MapFeature object (i.e. Plot(instance=instance)) """ feature_object_names = [ to_object_name(ft) for ft in feature.instance.map_feature_types ] if isinstance(feature, Convertible): # We're going to always work in display units here feature.convert_to_display_units() def set_attr_on_model(model, attr, val): field_classname = \ model._meta.get_field_by_name(attr)[0].__class__.__name__ if field_classname.endswith('PointField'): srid = val.get('srid', 3857) val = Point(val['x'], val['y'], srid=srid) val.transform(3857) elif field_classname.endswith('MultiPolygonField'): srid = val.get('srid', 4326) val = MultiPolygon(Polygon(val['polygon'], srid=srid), srid=srid) val.transform(3857) if attr == 'mapfeature_ptr': if model.mapfeature_ptr_id != value: raise Exception('You may not change the mapfeature_ptr_id') elif attr == 'id': if val != model.pk: raise Exception("Can't update id attribute") elif attr.startswith('udf:'): udf_name = attr[4:] if udf_name in [ field.name for field in model.get_user_defined_fields() ]: model.udfs[udf_name] = val else: raise KeyError('Invalid UDF %s' % attr) elif attr in model.fields(): model.apply_change(attr, val) else: raise Exception('Malformed request - invalid field %s' % attr) def save_and_return_errors(thing, user): try: if isinstance(thing, Convertible): thing.convert_to_database_units() thing.save_with_user(user) return {} except ValidationError as e: return package_field_errors(thing._model_name, e) tree = None rev_updates = ['universal_rev'] old_geom = feature.geom for (identifier, value) in request_dict.iteritems(): split_template = 'Malformed request - invalid field %s' object_name, field = dotted_split(identifier, 2, failure_format_string=split_template) if (object_name not in feature_object_names + ['tree']): raise Exception(split_template % identifier) tree_udfc_names = [ fdef.canonical_name for fdef in udf_defs(feature.instance, 'Tree') if fdef.iscollection ] if ((field in tree_udfc_names and feature.safe_get_current_tree() is None and value == [])): continue elif object_name in feature_object_names: model = feature elif object_name == 'tree' and feature.feature_type == 'Plot': # Get the tree or spawn a new one if needed tree = (tree or feature.safe_get_current_tree() or Tree(instance=feature.instance)) # We always edit in display units tree.convert_to_display_units() model = tree if field == 'species' and value: value = get_object_or_404(Species, instance=feature.instance, pk=value) elif field == 'plot' and value == unicode(feature.pk): value = feature else: raise Exception('Malformed request - invalid model %s' % object_name) set_attr_on_model(model, field, value) field_class = model._meta.get_field_by_name(field)[0] if isinstance(field_class, GeometryField): rev_updates.append('geo_rev') rev_updates.append('eco_rev') elif identifier in ['tree.species', 'tree.diameter']: rev_updates.append('eco_rev') errors = {} if feature.fields_were_updated(): errors.update(save_and_return_errors(feature, user)) if tree and tree.fields_were_updated(): tree.plot = feature errors.update(save_and_return_errors(tree, user)) if errors: # It simplifies the templates and client-side logic if the geometry # field errors are returned under the generic name if feature.geom_field_name in errors: errors['mapFeature.geom'] = errors[feature.geom_field_name] raise ValidationError(errors) if old_geom is not None and feature.geom != old_geom: update_hide_at_zoom_after_move(feature, user, old_geom) feature.instance.update_revs(*rev_updates) return feature, tree
def move_and_assert_counts(self, plot, new_point, expected_count_dict): old_point = plot.geom plot.geom = Point(*new_point) plot.save_with_user(self.user) update_hide_at_zoom_after_move(plot, self.user, old_point) self.assert_counts(expected_count_dict)
def update_map_feature(request_dict, user, feature): """ Update a map feature. Expects JSON in the request body to be: {'model.field', ...} Where model is either 'tree', 'plot', or another map feature type and field is any field on the model. UDF fields should be prefixed with 'udf:'. This method can be used to create a new map feature by passing in an empty MapFeature object (i.e. Plot(instance=instance)) """ feature_object_names = [to_object_name(ft) for ft in feature.instance.map_feature_types] if isinstance(feature, Convertible): # We're going to always work in display units here feature.convert_to_display_units() def set_attr_on_model(model, attr, val): field_classname = \ model._meta.get_field_by_name(attr)[0].__class__.__name__ if field_classname.endswith('PointField'): srid = val.get('srid', 3857) val = Point(val['x'], val['y'], srid=srid) val.transform(3857) elif field_classname.endswith('MultiPolygonField'): srid = val.get('srid', 4326) val = MultiPolygon(Polygon(val['polygon'], srid=srid), srid=srid) val.transform(3857) if attr == 'mapfeature_ptr': if model.mapfeature_ptr_id != value: raise Exception( 'You may not change the mapfeature_ptr_id') elif attr == 'id': if val != model.pk: raise Exception("Can't update id attribute") elif attr.startswith('udf:'): udf_name = attr[4:] if udf_name in [field.name for field in model.get_user_defined_fields()]: model.udfs[udf_name] = val else: raise KeyError('Invalid UDF %s' % attr) elif attr in model.fields(): model.apply_change(attr, val) else: raise Exception('Malformed request - invalid field %s' % attr) def save_and_return_errors(thing, user): try: if isinstance(thing, Convertible): thing.convert_to_database_units() thing.save_with_user(user) return {} except ValidationError as e: return package_field_errors(thing._model_name, e) tree = None rev_updates = ['universal_rev'] old_geom = feature.geom for (identifier, value) in request_dict.iteritems(): split_template = 'Malformed request - invalid field %s' object_name, field = dotted_split(identifier, 2, failure_format_string=split_template) if (object_name not in feature_object_names + ['tree']): raise Exception(split_template % identifier) tree_udfc_names = [fdef.canonical_name for fdef in udf_defs(feature.instance, 'Tree') if fdef.iscollection] if ((field in tree_udfc_names and feature.safe_get_current_tree() is None and value == [])): continue elif object_name in feature_object_names: model = feature elif object_name == 'tree' and feature.feature_type == 'Plot': # Get the tree or spawn a new one if needed tree = (tree or feature.safe_get_current_tree() or Tree(instance=feature.instance)) # We always edit in display units tree.convert_to_display_units() model = tree if field == 'species' and value: value = get_object_or_404(Species, instance=feature.instance, pk=value) elif field == 'plot' and value == unicode(feature.pk): value = feature else: raise Exception( 'Malformed request - invalid model %s' % object_name) set_attr_on_model(model, field, value) field_class = model._meta.get_field_by_name(field)[0] if isinstance(field_class, GeometryField): rev_updates.append('geo_rev') rev_updates.append('eco_rev') elif identifier in ['tree.species', 'tree.diameter']: rev_updates.append('eco_rev') errors = {} if feature.fields_were_updated(): errors.update(save_and_return_errors(feature, user)) if tree and tree.fields_were_updated(): tree.plot = feature errors.update(save_and_return_errors(tree, user)) if errors: # It simplifies the templates and client-side logic if the geometry # field errors are returned under the generic name if feature.geom_field_name in errors: errors['mapFeature.geom'] = errors[feature.geom_field_name] raise ValidationError(errors) if old_geom is not None and feature.geom != old_geom: update_hide_at_zoom_after_move(feature, user, old_geom) feature.instance.update_revs(*rev_updates) return feature, tree
def move_and_assert_counts(self, plot, new_point, expected_count_dict): old_point = plot.geom plot.geom = Point(*new_point) plot.save_with_user(self.user) update_hide_at_zoom_after_move(plot, self.user, old_point) self.assert_counts(expected_count_dict)