Example #1
0
    def test_record_is_created_when_nullables_are_still_pending(self):
        new_plot = Plot(geom=self.p1, instance=self.instance)
        new_plot.save_with_user(self.pending_user)

        new_tree = Tree(plot=new_plot, instance=self.instance,
                        diameter=10, height=10, readonly=False)

        new_tree.save_with_user(self.pending_user)

        approve_or_reject_audits_and_apply(
            new_plot.audits(),
            self.commander_user, True)

        insert_audit = Audit.objects.filter(model='Tree')\
                                    .get(field='id')
        field_audits = Audit.objects.filter(model='Tree')\
                                    .filter(field__in=['readonly', 'diameter',
                                                       'plot'])
        for audit in field_audits:
            approve_or_reject_audit_and_apply(
                audit, self.commander_user, approved=True)

        approve_or_reject_audit_and_apply(insert_audit,
                                          self.commander_user, True)

        real_tree = Tree.objects.get(pk=new_tree.pk)

        self.assertEqual(real_tree.plot_id, new_plot.pk)
        self.assertEqual(real_tree.diameter, 10)
        self.assertEqual(real_tree.height, None)
        self.assertNotEqual(real_tree.readonly, True)
Example #2
0
class UserCanDeleteTestCase(OTMTestCase):
    def setUp(self):
        instance = make_instance()

        self.creator_user = make_officer_user(instance)
        self.admin_user = make_admin_user(instance)
        self.other_user = make_officer_user(instance, username='******')

        self.plot = Plot(geom=instance.center, instance=instance)
        self.plot.save_with_user(self.creator_user)

        self.tree = Tree(plot=self.plot, instance=instance)
        self.tree.save_with_user(self.creator_user)

        self.rainBarrel = RainBarrel(geom=instance.center, instance=instance,
                                     capacity=5)
        self.rainBarrel.save_with_user(self.creator_user)

    def assert_can_delete(self, user, deletable, should_be_able_to_delete):
        can = deletable.user_can_delete(user)
        self.assertEqual(can, should_be_able_to_delete)

    def test_user_can_delete(self):
        self.assert_can_delete(self.creator_user, self.plot, True)
        self.assert_can_delete(self.admin_user, self.plot, True)
        self.assert_can_delete(self.other_user, self.plot, False)
        self.assert_can_delete(self.creator_user, self.rainBarrel, True)
        self.assert_can_delete(self.admin_user, self.rainBarrel, True)
        self.assert_can_delete(self.other_user, self.rainBarrel, False)
        self.assert_can_delete(self.creator_user, self.tree, True)
        self.assert_can_delete(self.admin_user, self.tree, True)
        self.assert_can_delete(self.other_user, self.tree, False)
Example #3
0
    def _commit_tree_data(self, data, plot, tree, tree_edited):
        for tree_attr, field_name in TreeImportRow.TREE_MAP.iteritems():
            value = data.get(field_name, None)
            if value:
                tree_edited = True
                if tree is None:
                    tree = Tree(instance=plot.instance)
                setattr(tree, tree_attr, value)

        ie = self.import_event
        tree_udf_defs = udf_defs(ie.instance, 'Tree')
        for udf_def in tree_udf_defs:
            udf_column_name = ie.get_udf_column_name(udf_def)
            value = data.get(udf_column_name, None)
            # Legitimate values could be falsey
            if value is not None:
                tree_edited = True
                if tree is None:
                    tree = Tree(instance=plot.instance)
                tree.udfs[udf_def.name] = \
                    self._import_value_to_udf_value(udf_def, value)

        if tree_edited:
            tree.plot = plot
            tree.save_with_system_user_bypass_auth()
            tree.plot.update_updated_fields(ie.owner)
Example #4
0
    def handle(self, *args, **options):
        """ Create some seed data """
        instance, user = self.setup_env(*args, **options)

        species_qs = instance.scope_model(Species)

        n = options['n']
        self.stdout.write("Will create %s plots" % n)

        get_prob = lambda option: float(min(100, max(0, option))) / 100.0
        tree_prob = get_prob(options['ptree'])
        species_prob = get_prob(options['pspecies'])
        diameter_prob = get_prob(options['pdiameter'])
        max_radius = options['radius']

        center_x = instance.center.x
        center_y = instance.center.y

        import_event = ImportEvent(imported_by=user)
        import_event.save()

        ct = 0
        cp = 0
        for i in xrange(0, n):
            mktree = random.random() < tree_prob
            radius = random.gauss(0.0, max_radius)
            theta = random.random() * 2.0 * math.pi

            x = math.cos(theta) * radius + center_x
            y = math.sin(theta) * radius + center_y

            plot = Plot(instance=instance,
                        geom=Point(x, y),
                        import_event=import_event)

            plot.save_with_user(user)
            cp += 1

            if mktree:
                add_species = random.random() < species_prob
                if add_species:
                    species = random.choice(species_qs)
                else:
                    species = None

                add_diameter = random.random() < diameter_prob
                if add_diameter:
                    diameter = 2 + random.random() * 18
                else:
                    diameter = None

                tree = Tree(plot=plot,
                            import_event=import_event,
                            species=species,
                            diameter=diameter,
                            instance=instance)
                tree.save_with_user(user)
                ct += 1

        self.stdout.write("Created %s trees and %s plots" % (ct, cp))
    def test_treephoto_overrides_tree_and_plot(self):
        tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
        tree.save_with_user(self.user)
        tree.add_photo(self.image, self.other)

        self.clear_and_set_and_reload()
        self.assertEqual(self.plot.updated_by_id, self.other.pk)
Example #6
0
    def test_delete_tree(self):
        plot = Plot(instance=self.instance,
                    geom=Point(0, 0))
        plot.save_with_user(self.user)
        tree = Tree(instance=self.instance,
                    plot=plot)
        tree.save_with_user(self.user)

        self.assertEqual(Plot.objects.count(), 1)
        self.assertEqual(Tree.objects.count(), 1)

        self._login_workflow()
        self._go_to_plot_detail(plot.pk)
        self.select_buttons()
        self.assertCantClickDeleteOrCancel()
        self.assertEqual(Plot.objects.count(), 1)
        self.assertEqual(Tree.objects.count(), 1)

        self.delete_begin.click()
        self.delete_cancel.click()
        self.assertCantClickDeleteOrCancel()
        self.assertEqual(Plot.objects.count(), 1)
        self.assertEqual(Tree.objects.count(), 1)

        self.select_buttons()
        self.delete_begin.click()
        self.delete_confirm.click()
        sleep(DATABASE_COMMIT_DELAY)
        self.assertEqual(Plot.objects.count(), 1)
        self.assertEqual(Tree.objects.count(), 0)
Example #7
0
def _plot_audits(user, instance, plot):
    fake_tree = Tree(instance=instance)
    tree_visible_fields = fake_tree.visible_fields(user)

    # Get a history of trees that were on this plot
    tree_history = plot.get_tree_history()

    tree_filter = Q(model='Tree',
                    field__in=tree_visible_fields,
                    model_id__in=tree_history)

    tree_delete_filter = Q(model='Tree',
                           action=Audit.Type.Delete,
                           model_id__in=tree_history)

    tree_collection_udfs_audit_names =\
        fake_tree.visible_collection_udfs_audit_names(user)

    tree_collection_udfs_filter = Q(
        model__in=tree_collection_udfs_audit_names,
        model_id__in=Tree.static_collection_udfs_audit_ids(
            (instance,), tree_history, tree_collection_udfs_audit_names))

    filters = [tree_filter, tree_delete_filter]
    cudf_filters = [tree_collection_udfs_filter]

    audits = _map_feature_audits(user, instance, plot, filters, cudf_filters)

    return audits
Example #8
0
    def test_treephoto_hash_to_model(self):
        plot = Plot(geom=Point(0, 0), instance=self.instance)
        plot.save_with_user(self.commander)
        tree = Tree(plot=plot, instance=self.instance)
        tree.save_with_user(self.commander)

        ipath = self.resource_path('tree1.gif')

        tp_dict = json.loads(self.photo_blob % ipath)

        self.assertEqual(TreePhoto.objects.count(), 0)

        save_treephoto_blank = partial(save_treephoto, MIGRATION_RULES, '')

        hashes_to_saved_objects(
            MIGRATION_RULES,
            "treephoto", [tp_dict],
            {'tree': {1: tree.pk},
             'user': {1: self.commander.pk}},
            save_treephoto_blank,
            self.instance)

        self.assertEqual(TreePhoto.objects.count(), 1)
        photo = TreePhoto.objects.all()[0]

        self.assertIsNotNone(photo.image)
        self.assertIsNotNone(photo.thumbnail)
Example #9
0
    def setUp(self):
        super(ExportTreeTaskTest, self).setUp()

        set_write_permissions(self.instance, self.user,
                              'Plot', ['udf:Test choice'])
        set_write_permissions(self.instance, self.user,
                              'Tree', ['udf:Test int'])

        UserDefinedFieldDefinition.objects.create(
            instance=self.instance,
            model_type='Plot',
            datatype=json.dumps({'type': 'choice',
                                 'choices': ['a', 'b', 'c']}),
            iscollection=False,
            name='Test choice')

        UserDefinedFieldDefinition.objects.create(
            instance=self.instance,
            model_type='Tree',
            datatype=json.dumps({'type': 'int'}),
            iscollection=False,
            name='Test int')

        p = Plot(geom=self.instance.center, instance=self.instance,
                 address_street="123 Main Street")
        p.udfs['Test choice'] = 'a'

        p.save_with_user(self.user)

        t = Tree(plot=p, instance=self.instance, diameter=2)
        t.udfs['Test int'] = 4

        t.save_with_user(self.user)
Example #10
0
def add_tree_photo_helper(request, instance, feature_id, tree_id=None):
    plot = get_map_feature_or_404(feature_id, instance, 'Plot')
    tree_ids = [t.pk for t in plot.tree_set.all()]

    if tree_id and int(tree_id) in tree_ids:
        tree = Tree.objects.get(pk=tree_id)
    elif tree_id is None:
        # See if a tree already exists on this plot
        tree = plot.current_tree()

        if tree is None:
            # A tree doesn't exist, create a new tree create a
            # new tree, and attach it to this plot
            tree = Tree(plot=plot, instance=instance)

            # TODO: it is possible that a user has the ability to
            # 'create tree photos' but not trees. In this case we
            # raise an authorization exception here.
            # It is, however, possible to have both a pending
            # tree and a pending tree photo
            # This will be added later, when auth/admin work
            # correctly with this system
            tree.save_with_user(request.user)

    else:
        # Tree id is invalid or not in this plot
        raise Http404('Tree id %s not found on plot %s'
                      % (tree_id, feature_id))

    #TODO: Auth Error
    data = get_image_from_request(request)
    treephoto = tree.add_photo(data, request.user)

    return treephoto, tree
Example #11
0
class WritableTest(PermissionsTestCase):
    def test_plot_is_writable_if_can_create_tree(self):
        self.commander_user = make_commander_user(self.instance)
        self.commander_role = \
            self.commander_user.get_instance_user(self.instance).role
        self.tree_only_user = make_user(self.instance)
        self.tree_only_role = self.instance.default_role

        content_type = ContentType.objects.get_for_model(Tree)
        add_tree_perm = Permission.objects.get(content_type=content_type,
                                               codename='add_tree')
        self.tree_only_role.instance_permissions.add(add_tree_perm)
        self.tree_only_role.save()

        self.p = Point(-8515941.0, 4953519.0)
        self.plot = Plot(instance=self.instance, width=12, geom=self.p)
        self.plot.save_with_user(self.commander_user)

        plot2 = Plot(instance=self.instance, width=12, geom=self.p)
        self.assertRaises(AuthorizeException,
                          plot2.save_with_user,
                          self.tree_only_user)

        self.tree = Tree(instance=self.instance, plot=self.plot)
        self.tree.save_with_user(self.tree_only_user)

        self.assertTrue(self.tree.user_can_create(self.tree_only_user))

        # The plot should be writable if the user can create a tree
        self.assertTrue(perms.map_feature_is_writable(
            self.tree_only_role,
            self.plot))
Example #12
0
    def setUp(self):
        super(ExportTreeTaskTest, self).setUp()

        set_write_permissions(self.instance, self.user, "Plot", ["udf:Test choice"])
        set_write_permissions(self.instance, self.user, "Tree", ["udf:Test int"])

        UserDefinedFieldDefinition.objects.create(
            instance=self.instance,
            model_type="Plot",
            datatype=json.dumps({"type": "choice", "choices": ["a", "b", "c"]}),
            iscollection=False,
            name="Test choice",
        )

        UserDefinedFieldDefinition.objects.create(
            instance=self.instance,
            model_type="Tree",
            datatype=json.dumps({"type": "int"}),
            iscollection=False,
            name="Test int",
        )

        p = Plot(geom=self.instance.center, instance=self.instance, address_street="123 Main Street")
        p.udfs["Test choice"] = "a"

        p.save_with_user(self.user)

        t = Tree(plot=p, instance=self.instance, diameter=2)
        t.udfs["Test int"] = 4

        t.save_with_user(self.user)
Example #13
0
 def make_tree(self, user=None):
     user = user or make_commander_user(self.instance)
     plot = Plot(geom=self.instance.center, instance=self.instance)
     plot.save_with_user(user)
     tree = Tree(instance=self.instance, plot=plot)
     tree.save_with_user(user)
     return tree
Example #14
0
    def setUp(self):
        super(ExportTreeTaskTest, self).setUp()

        p = Plot(geom=Point(0, 0), instance=self.instance, address_street="123 Main Street")
        p.save_with_user(self.user)

        t = Tree(plot=p, instance=self.instance, diameter=2)
        t.save_with_user(self.user)
Example #15
0
 def test_reputations_increase_for_direct_writes(self):
     self.assertEqual(self.privileged_user.get_reputation(self.instance), 0)
     t = Tree(plot=self.plot, instance=self.instance,
              readonly=True)
     t.save_with_user(self.privileged_user)
     user = User.objects.get(pk=self.privileged_user.id)
     reputation = user.get_reputation(self.instance)
     self.assertGreater(reputation, 0)
Example #16
0
    def test_delete_tree_sets_updated(self):
        tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
        tree.save_with_user(self.user)
        self.plot = Plot.objects.get(pk=self.plot.pk)
        self.inital_updated = self.plot.updated_at

        tree.delete_with_user(self.user)
        self.assertGreater(self.plot.updated_at, self.initial_updated)
Example #17
0
 def test_update_species(self):
     with self._assert_updates_eco_rev(True):
         tree = Tree(instance=self.instance, plot=self.plot)
         tree.save_with_user(self.user)
         species = Species(common_name='foo', instance=self.instance)
         species.save_with_user(self.user)
         request_dict = {'tree.species': species.pk}
         update_map_feature(request_dict, self.user, self.plot)
Example #18
0
class EcoTest(TestCase):
    def setUp(self):
        self.factory = RequestFactory()

        self.instance, system_user = tm.make_instance_and_system_user()

        self.user = User(username="******")
        self.user.save_with_user(system_user)
        self.user.roles.add(tm.make_commander_role(self.instance))

        self.species = Species(symbol='CEDR',
                               genus='cedrus',
                               species='atlantica',
                               max_dbh=2000,
                               max_height=100)
        self.species.save()

        p1 = Point(-8515941.0, 4953519.0)
        self.plot = Plot(geom=p1,
                         instance=self.instance,
                         created_by=self.user)

        self.plot.save_with_user(self.user)

        self.tree = Tree(plot=self.plot,
                         instance=self.instance,
                         readonly=False,
                         species=self.species,
                         diameter=1630,
                         created_by=self.user)

        self.tree.save_with_user(self.user)

    def test_group_eco(self):
        pass  # TODO: Once filtering has been enabled

    def test_eco_benefit_sanity(self):
        req = self.factory.get('/%s/eco/benefit/tree/%s/' %
                              (self.instance.pk, self.tree.pk))

        response = tree_benefits(req,
                                 instance_id=self.instance.pk,
                                 tree_id=self.tree.pk,
                                 region='NoEastXXX')

        self.assertEqual(response.status_code, 200)
        rslt = json.loads(response.content)

        bens = rslt['benefits']

        def assertBValue(benefit, unit, value):
            self.assertEqual(bens[benefit]['unit'], unit)
            self.assertEqual(int(float(bens[benefit]['value'])), value)

        assertBValue('energy', 'kwh', 1896)
        assertBValue('airquality', 'lbs/year', 6)
        assertBValue('stormwater', 'gal', 3185)
        assertBValue('co2', 'lbs/year', 563)
Example #19
0
    def test_save_new_object_authorized(self):
        '''Save two new objects with authorized user, nothing should happen'''
        plot = Plot(geom=self.p1, instance=self.instance)

        plot.save_with_user(self.officer)

        tree = Tree(plot=plot, instance=self.instance)

        tree.save_with_user(self.officer)
Example #20
0
class UserCanDeleteTestCase(OTMTestCase):
    def setUp(self):
        instance = make_instance()
        # Fancy name, but no write, create, or delete permissions
        instance.default_role.name = Role.ADMINISTRATOR

        self.creator_user = make_officer_user(instance)
        self.admin_user = make_admin_user(instance)
        self.other_user = make_observer_user(instance, username='******')
        self.tweaker_user = make_tweaker_user(instance)
        self.conjurer_user = make_conjurer_user(instance)

        self.plot = Plot(geom=instance.center, instance=instance)
        self.plot.save_with_user(self.creator_user)

        self.tree = Tree(plot=self.plot, instance=instance)
        self.tree.save_with_user(self.creator_user)

        self.rainBarrel = RainBarrel(geom=instance.center, instance=instance,
                                     capacity=5)
        self.rainBarrel.save_with_user(self.creator_user)

    def assert_can_delete(self, user, deletable, should_be_able_to_delete):
        can = deletable.user_can_delete(user)
        self.assertEqual(can, should_be_able_to_delete)

    def test_user_can_delete(self):
        self.assert_can_delete(self.conjurer_user, self.plot, True)
        self.assert_can_delete(self.conjurer_user, self.rainBarrel, True)
        self.assert_can_delete(self.conjurer_user, self.tree, True)

        self.assert_can_delete(self.creator_user, self.plot, True)
        self.assert_can_delete(self.creator_user, self.rainBarrel, True)
        self.assert_can_delete(self.creator_user, self.tree, True)

        self.assert_can_delete(self.admin_user, self.plot, True)
        self.assert_can_delete(self.admin_user, self.rainBarrel, True)
        self.assert_can_delete(self.admin_user, self.tree, True)

    def test_user_cannot_delete(self):
        self.assert_can_delete(self.tweaker_user, self.plot, False)
        self.assert_can_delete(self.tweaker_user, self.rainBarrel, False)
        self.assert_can_delete(self.tweaker_user, self.tree, False)

        self.assert_can_delete(self.other_user, self.plot, False)
        self.assert_can_delete(self.other_user, self.rainBarrel, False)
        self.assert_can_delete(self.other_user, self.tree, False)

    def test_admin_cannot_delete_by_flag(self):
        instance = self.tree.get_instance()
        role = self.admin_user.get_role(instance)
        role.instance_permissions.clear()

        self.assertTrue(self.admin_user.get_instance_user(instance).admin)
        self.assertEqual(role.instance_permissions.count(), 0)

        self.assert_can_delete(self.admin_user, self.tree, False)
Example #21
0
    def _test_hash_setup(self):
        self.initial_plot_hash = self.plot_obj.hash
        self.initial_map_feature_hash = self.map_feature_obj.hash

        # adding a tree should change the plot hash
        tree = Tree(diameter=10, plot=self.plot_obj, instance=self.instance)
        tree.save_with_user(self.user)
        self.final_plot_hash = self.plot_obj.hash
        self.final_map_feature_hash = self.map_feature_obj.hash
Example #22
0
    def test_delete_tree_sets_updated(self):
        tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
        tree.save_with_user(self.user)
        self.plot.refresh_from_db()
        self.inital_updated = self.plot.updated_at

        tree.delete_with_user(self.fellow)
        self.plot.refresh_from_db()
        self.assertGreater(self.plot.updated_at, self.initial_updated)
        self.assertEqual(self.plot.updated_by, self.fellow)
Example #23
0
 def test_add_photo_sets_updated(self):
     tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
     tree.save_with_user(self.user)
     photo = TreePhoto(instance=self.instance,
                       map_feature=self.plot, tree=tree)
     photo.set_image(self.load_resource('tree1.gif'))
     photo.save_with_user(self.fellow)
     self.plot.refresh_from_db()
     self.assertGreater(self.plot.updated_at, self.initial_updated)
     self.assertEqual(self.plot.updated_by, self.fellow)
Example #24
0
File: views.py Project: heath/OTM2
def _plot_audits(user, instance, plot):
    readable_plot_fields = plot.visible_fields(user)

    plot_filter = Q(model='Plot', model_id=plot.pk,
                    field__in=readable_plot_fields)

    plot_collection_udfs_filter = Q(
        model__in=plot.visible_collection_udfs_audit_names(user),
        model_id__in=plot.collection_udfs_audit_ids())

    fake_tree = Tree(instance=instance)
    tree_visible_fields = fake_tree.visible_fields(user)

    # Get a history of trees that were on this plot
    tree_history = plot.get_tree_history()

    tree_filter = Q(model='Tree',
                    field__in=tree_visible_fields,
                    model_id__in=tree_history)

    tree_delete_filter = Q(model='Tree',
                           action=Audit.Type.Delete,
                           model_id__in=tree_history)

    tree_collection_udfs_audit_names =\
        fake_tree.visible_collection_udfs_audit_names(user)

    tree_collection_udfs_filter = Q(
        model__in=tree_collection_udfs_audit_names,
        model_id__in=Tree.static_collection_udfs_audit_ids(
            (instance,), tree_history, tree_collection_udfs_audit_names))

    # Seems to be much faster to do three smaller
    # queries here instead of ORing them together
    # (about a 50% inprovement!)
    # TODO: Verify this is still the case now that we are also getting
    # collection udf audits
    iaudit = Audit.objects.filter(instance=instance)

    audits = []
    for afilter in [tree_filter, tree_delete_filter, plot_filter]:
        audits += list(iaudit.filter(afilter).order_by('-updated')[:5])

    # UDF collection audits have some fields which aren't very useful to show
    udf_collection_exclude_filter = Q(
        field__in=['model_id', 'field_definition'])

    for afilter in [plot_collection_udfs_filter, tree_collection_udfs_filter]:
        audits += list(iaudit.filter(afilter)
                             .exclude(udf_collection_exclude_filter)
                             .order_by('-updated')[:5])

    audits = sorted(audits, key=lambda audit: audit.updated, reverse=True)[:5]

    return audits
Example #25
0
def mkTree(u, plot=None, species=None):
    if not plot:
        plot = mkPlot(i)

    if not species:
        species = Species.objects.all()[0]

    t = Tree(plot=plot, species=species, last_updated_by=u, import_event=ImportEvent.objects.all()[0])
    t.save()

    return t
Example #26
0
    def test_tree_overrides_plot(self):
        tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
        tree.save_with_user(self.user)

        tree_audit = self.max_audit_for_model_type('Tree')
        plot_audit = self.max_audit_for_model_type('Plot')
        # Backdate the plot audit so it is definitely older than the tree audit
        plot_audit.created = tree_audit.created - timedelta(days=1)
        plot_audit.save()

        self.clear_and_set_and_reload()
        self.assertEqual(self.plot.updated_at, tree_audit.created)
Example #27
0
    def test_plot_with_tree_always_shows_tree_details(self):
        plot = Plot(instance=self.instance, geom=self.instance.center)
        plot.save_with_user(self.user)
        tree = Tree(plot=plot, diameter=10, instance=self.instance)
        tree.save_with_user(self.user)

        self.login_workflow()
        self.go_to_feature_detail(plot.pk)
        self._select_elements()
        self.edit_plot.click()
        self.cancel_edit.click()
        self.assertElementVisibility(self.tree_details_section, visible=True)
Example #28
0
 def test_user_can_create_tree_photo(self):
     self._add_builtin_permission(self.role_yes, TreePhoto, 'add_treephoto')
     commander = make_commander_user(self.instance)
     plot = Plot(instance=self.instance, geom=self.p)
     plot.save_with_user(commander)
     tree = Tree(plot=plot, instance=self.instance)
     tree.save_with_user(commander)
     user_yes = make_user(instance=self.instance,
                          make_role=lambda inst: self.role_yes)
     photo = TreePhoto(instance=self.instance,
                       map_feature=plot, tree=tree)
     photo.set_image(self.load_resource('tree1.gif'))
     self.assertTrue(photo.user_can_create(user_yes))
Example #29
0
def mkTree(instance, user, plot=None, species=None):
    if not plot:
        plot = mkPlot(instance, user)

    if species is not None:
        s = Species.objects.all()[0]
    else:
        s = species

    t = Tree(plot=plot, instance=instance, species=s)
    t.save_with_user(user)

    return t
Example #30
0
    def test_user_cannot_delete_tree_photo(self):
        commander = make_commander_user(self.instance)
        plot = Plot(instance=self.instance, geom=self.p)
        plot.save_with_user(commander)
        tree = Tree(plot=plot, instance=self.instance)
        tree.save_with_user(commander)
        image = self.load_resource('tree1.gif')

        photo = tree.add_photo(image, commander)

        user_no = make_user(instance=self.instance,
                            make_role=lambda inst: self.role_no)
        self.assertFalse(photo.user_can_delete(user_no))
Example #31
0
class EcoTest(UrlTestCase):
    def setUp(self):
        # Example url for
        # CEAT, 1630 dbh, NoEastXXX
        # eco.json?otmcode=CEAT&diameter=1630&region=NoEastXXX
        def mockbenefits(*args, **kwargs):
            benefits = {
                "Benefits": {
                    "aq_nox_avoided": 0.6792,
                    "aq_nox_dep": 0.371,
                    "aq_ozone_dep": 0.775,
                    "aq_pm10_avoided": 0.0436,
                    "aq_pm10_dep": 0.491,
                    "aq_sox_avoided": 0.372,
                    "aq_sox_dep": 0.21,
                    "aq_voc_avoided": 0.0254,
                    "bvoc": -0.077,
                    "co2_avoided": 255.5,
                    "co2_sequestered": 0,
                    "co2_storage": 6575,
                    "electricity": 187,
                    "hydro_interception": 12.06,
                    "natural_gas": 5834.1
                }
            }
            return (benefits, None)

        region = ITreeRegion.objects.get(code='NoEastXXX')
        p = region.geometry.point_on_surface

        self.instance = make_instance(is_public=True, point=p)
        self.user = make_commander_user(self.instance)

        self.species = Species(otm_code='CEAT',
                               genus='cedrus',
                               species='atlantica',
                               max_diameter=2000,
                               max_height=100,
                               instance=self.instance)

        self.species.save_with_user(self.user)

        self.plot = Plot(geom=p, instance=self.instance)

        self.plot.save_with_user(self.user)

        self.tree = Tree(plot=self.plot,
                         instance=self.instance,
                         readonly=False,
                         species=self.species,
                         diameter=1630)

        self.tree.save_with_user(self.user)

        self.origBenefitFn = ecobackend.json_benefits_call
        ecobackend.json_benefits_call = mockbenefits

    def tearDown(self):
        ecobackend.json_benefits_call = self.origBenefitFn

    def assert_benefit_value(self, bens, benefit, unit, value):
        self.assertEqual(bens[benefit]['unit'], unit)
        self.assertEqual(int(float(bens[benefit]['value'])), value)

    def test_eco_benefit_sanity(self):
        rslt, basis, error = TreeBenefitsCalculator()\
            .benefits_for_object(self.instance, self.tree.plot)

        bens = rslt['plot']

        self.assert_benefit_value(bens, BenefitCategory.ENERGY, 'kwh', 1896)
        self.assert_benefit_value(bens, BenefitCategory.AIRQUALITY, 'lbs', 6)
        self.assert_benefit_value(bens, BenefitCategory.STORMWATER, 'gal',
                                  3185)
        self.assert_benefit_value(bens, BenefitCategory.CO2, 'lbs', 563)
        self.assert_benefit_value(bens, BenefitCategory.CO2STORAGE, 'lbs',
                                  6575)

    def testSearchBenefits(self):
        request = make_request(
            {'q': json.dumps({'tree.readonly': {
                'IS': False
            }})})  # all trees
        request.instance_supports_ecobenefits = self.instance\
                                                    .has_itree_region()
        result = search_tree_benefits(request, self.instance)

        benefits = result['benefits']

        self.assertTrue(len(benefits) > 0)

    def test_group_basis_empty(self):
        basis = {}
        example = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 8
            },
            'group2': {
                'n_objects_used': 10,
                'n_objects_discarded': 12
            }
        }

        _combine_benefit_basis(basis, example)
        self.assertEqual(basis, example)

    def test_group_basis_combine_new_group(self):
        # New groups are added
        basis = {'group1': {'n_objects_used': 5, 'n_objects_discarded': 8}}
        new_group = {
            'group2': {
                'n_objects_used': 13,
                'n_objects_discarded': 4
            }
        }
        target = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 8
            },
            'group2': {
                'n_objects_used': 13,
                'n_objects_discarded': 4
            }
        }
        _combine_benefit_basis(basis, new_group)
        self.assertEqual(basis, target)

    def test_group_basis_combine_existing_groups(self):
        basis = {'group1': {'n_objects_used': 5, 'n_objects_discarded': 8}}
        update_group = {
            'group1': {
                'n_objects_used': 13,
                'n_objects_discarded': 4
            }
        }
        target = {'group1': {'n_objects_used': 18, 'n_objects_discarded': 12}}
        _combine_benefit_basis(basis, update_group)
        self.assertEqual(basis, target)

    def test_combine_benefit_groups_empty(self):
        # with and without currency
        base_group = {
            'group1': {
                'benefit1': {
                    'value': 3,
                    'currency': 9,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 3,
                    'currency': 9,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        groups = {}
        _combine_grouped_benefits(groups, base_group)

        self.assertEqual(groups, base_group)

    def test_combine_benefit_groups_no_overlap(self):
        base_group = {
            'group1': {
                'benefit1': {
                    'value': 3,
                    'currency': 9,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 4,
                    'currency': 10,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        new_group = {
            'group2': {
                'benefit1': {
                    'value': 5,
                    'currency': 11,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 6,
                    'currency': 19,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        groups = {}
        _combine_grouped_benefits(groups, base_group)
        _combine_grouped_benefits(groups, new_group)

        target = {
            'group1': base_group['group1'],
            'group2': new_group['group2']
        }

        self.assertEqual(groups, target)

    def test_combine_benefit_groups_sums_benefits(self):
        base_group = {
            'group1': {
                'benefit1': {
                    'value': 3,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 4,
                    'currency': 10,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit3': {
                    'value': 32,
                    'currency': 919,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        new_group = {
            'group1': {
                'benefit1': {
                    'value': 5,
                    'currency': 11,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 7,
                    'unit': 'gal',
                    'currency': 19,
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit4': {
                    'value': 7,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        groups = {}
        _combine_grouped_benefits(groups, base_group)
        _combine_grouped_benefits(groups, new_group)

        target = {
            'group1': {
                'benefit1': {
                    'value': 8,
                    'currency': 11,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 11,
                    'currency': 29,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit3': {
                    'value': 32,
                    'currency': 919,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit4': {
                    'value': 7,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }

        self.assertEqual(groups, target)

    def test_annotates_basis(self):
        basis = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 15
            },
            'group2': {
                'n_objects_used': 2,
                'n_objects_discarded': 18
            }
        }
        target = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 15,
                'n_total': 20,
                'n_pct_calculated': 0.25
            },
            'group2': {
                'n_objects_used': 2,
                'n_objects_discarded': 18,
                'n_total': 20,
                'n_pct_calculated': 0.1
            }
        }
        _annotate_basis_with_extra_stats(basis)

        self.assertEqual(basis, target)
Example #32
0
class FilterParserTests(OTMTestCase):
    def _setup_tree_and_collection_udf(self):
        instance = make_instance()

        self.plotstew = make_collection_udf(instance,
                                            model='Plot',
                                            datatype=COLLECTION_UDF_DATATYPE)
        self.treestew = make_collection_udf(instance,
                                            model='Tree',
                                            datatype=COLLECTION_UDF_DATATYPE)

        commander = make_commander_user(instance)
        set_write_permissions(instance, commander, 'Plot', ['udf:Stewardship'])
        set_write_permissions(instance, commander, 'Tree', ['udf:Stewardship'])

        d1 = {'action': 'prune', 'date': "2014-05-3 00:00:00"}
        d2 = {'action': 'water', 'date': "2014-04-29 00:00:00"}

        self.plot = Plot(instance=instance, geom=instance.center)
        self.plot.udfs[self.plotstew.name] = [d1]
        self.plot.save_with_user(commander)

        self.tree = Tree(instance=instance, plot=self.plot)
        self.tree.udfs[self.treestew.name] = [d2]
        self.tree.save_with_user(commander)

    def destructure_query_set(self, node):
        """
        Django query objects are not comparable by themselves, but they
        are built from a tree (django.util.tree) and stored in nodes

        This function generates a canonical representation using sets and
        tuples of a query tree

        This can be used to verify that query structures are made correctly
        """
        if isinstance(node, Node):
            n = (node.connector,
                 frozenset(
                     {self.destructure_query_set(c)
                      for c in node.children}))

            if node.negated:
                n = ('NOT', n)

            return n
        elif isinstance(node, tuple):
            # Lists are unhashable, so convert ValuesListQuerySets into tuples
            # for easy comparison
            return tuple(
                tuple(c) if isinstance(c, ValuesListQuerySet) else c
                for c in node)
        else:
            return node

    def test_key_parser_plots(self):
        # Plots searches on plot go directly to a field
        match = search._parse_predicate_key('plot.width',
                                            mapping=search.DEFAULT_MAPPING)
        self.assertEqual(match, ('plot', 'width'))

    def test_key_parser_plots_with_tree_map(self):
        # Plots searches on tree go require a prefix
        match = search._parse_predicate_key('plot.width',
                                            mapping=search.TREE_MAPPING)
        self.assertEqual(match, ('plot', 'plot__width'))

    def test_udf_fields_look_good(self):
        match = search._parse_predicate_key('plot.udf:The 1st Planter',
                                            mapping=search.DEFAULT_MAPPING)
        self.assertEqual(match, ('plot', 'udf:The 1st Planter'))

    def test_key_parser_trees(self):
        # Tree searches on plot require a prefix and the field
        match = search._parse_predicate_key('tree.dbh',
                                            mapping=search.DEFAULT_MAPPING)
        self.assertEqual(match, ('tree', 'tree__dbh'))

    def test_key_parser_trees_with_tree_map(self):
        # Tree searches on tree go directly to the field
        match = search._parse_predicate_key('tree.dbh',
                                            mapping=search.TREE_MAPPING)
        self.assertEqual(match, ('tree', 'dbh'))

    def test_key_parser_tree_collection_udf(self):
        # UDF searches go on the specified model's id
        match = search._parse_predicate_key('udf:tree:52.action',
                                            mapping=search.TREE_MAPPING)
        self.assertEqual(match, ('udf:tree:52', 'id'))

    def test_key_parser_plot_collection_udf(self):
        # UDF searches go on the specified model's id
        match = search._parse_predicate_key('udf:plot:52.action',
                                            mapping=search.TREE_MAPPING)
        self.assertEqual(match, ('udf:plot:52', 'plot__id'))

    def test_key_parser_invalid_model(self):
        # Invalid models should raise an exception
        self.assertRaises(search.ParseException,
                          search._parse_predicate_key,
                          "user.id",
                          mapping=search.DEFAULT_MAPPING)

    def test_key_parser_too_many_dots(self):
        # Dotted fields are also not allowed
        self.assertRaises(search.ParseException,
                          search._parse_predicate_key,
                          "plot.width.other",
                          mapping=search.DEFAULT_MAPPING)

    def test_combinator_and(self):
        qa = Q(a=1)
        qb = Q(b=1)
        qc = Q(c=1)

        # Simple AND
        ands = search._apply_combinator('AND', [qa, qb, qc])

        self.assertEqual(self.destructure_query_set(ands),
                         self.destructure_query_set(qa & qb & qc))

    def test_combinator_or(self):
        qa = Q(a=1)
        qb = Q(b=1)
        qc = Q(c=1)

        # Simple OR
        ands = search._apply_combinator('OR', [qa, qb, qc])

        self.assertEqual(self.destructure_query_set(ands),
                         self.destructure_query_set(qa | qb | qc))

    def test_combinator_invalid_combinator(self):
        qa = Q(a=1)
        qb = Q(b=1)
        qc = Q(c=1)

        # Error if not AND,OR
        self.assertRaises(search.ParseException, search._apply_combinator,
                          'ANDarg', [qa, qb])

        self.assertRaises(search.ParseException, search._apply_combinator, qc,
                          [qa, qb])

    def test_combinator_invalid_empty(self):
        # Error if empty
        self.assertRaises(search.ParseException, search._apply_combinator,
                          'AND', [])

    def test_boundary_constraint(self):
        b = Boundary.objects.create(geom=MultiPolygon(make_simple_polygon(0)),
                                    name='whatever',
                                    category='whatever',
                                    sort_order=1)

        inparams = search._parse_dict_value({'IN_BOUNDARY': b.pk})
        self.assertEqual(inparams, {'__within': b.geom})

    def test_constraints_in(self):
        inparams = search._parse_dict_value({'IN': [1, 2, 3]})
        self.assertEqual(inparams, {'__in': [1, 2, 3]})

    def test_constraints_isnull(self):
        inparams = search._parse_dict_value({'ISNULL': True})
        self.assertEqual(inparams, {'__isnull': True})

    def test_constraints_is(self):
        # "IS" is a special case in that we don't need to appl
        # a suffix at all
        isparams = search._parse_dict_value({'IS': 'what'})
        self.assertEqual(isparams, {'': 'what'})

    def test_constraints_invalid_groups(self):
        # It is an error to combine mutually exclusive groups
        self.assertRaises(search.ParseException, search._parse_dict_value, {
            'IS': 'what',
            'IN': [1, 2, 3]
        })

        self.assertRaises(search.ParseException, search._parse_dict_value, {
            'IS': 'what',
            'MIN': 3
        })

    def test_constraints_invalid_keys(self):
        self.assertRaises(search.ParseException, search._parse_dict_value,
                          {'EXCLUSIVE': 9})

        self.assertRaises(search.ParseException, search._parse_dict_value,
                          {'IS NOT VALID KEY': 'what'})

    def test_contraint_min(self):
        const = search._parse_dict_value({'MIN': 5})
        self.assertEqual(const, {'__gte': 5})

    def test_contraint_max(self):
        const = search._parse_dict_value({'MAX': 5})
        self.assertEqual(const, {'__lte': 5})

    def test_contraint_max_with_exclusive(self):
        const = search._parse_dict_value(
            {'MAX': {
                'VALUE': 5,
                'EXCLUSIVE': True
            }})
        self.assertEqual(const, {'__lt': 5})

        const = search._parse_dict_value(
            {'MAX': {
                'VALUE': 5,
                'EXCLUSIVE': False
            }})
        self.assertEqual(const, {'__lte': 5})

    def test_constraints_min_and_max(self):
        const = search._parse_dict_value({
            'MIN': 5,
            'MAX': {
                'VALUE': 9,
                'EXCLUSIVE': False
            }
        })
        self.assertEqual(const, {'__lte': 9, '__gte': 5})

    def test_within_radius(self):
        const = search._parse_dict_value(
            {'WITHIN_RADIUS': {
                "RADIUS": 5,
                "POINT": {
                    "x": 100,
                    "y": 50
                }
            }})
        self.assertEqual(const, {'__dwithin': (Point(100, 50), Distance(m=5))})

    def test_parse_species_predicate(self):
        pred = search._parse_predicate(
            {
                'species.id': 113,
                'species.flowering': True
            },
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('tree__species__id', 113),
                          ('tree__species__flowering', True)})

        self.assertEqual(self.destructure_query_set(pred), target)

    def test_like_predicate(self):
        pred = search._parse_predicate(
            {'tree.steward': {
                'LIKE': 'thisisatest'
            }},
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('tree__steward__icontains', 'thisisatest')})

        self.assertEqual(self.destructure_query_set(pred), target)

    def test_parse_predicate(self):
        pred = search._parse_predicate(
            {
                'plot.width': {
                    'MIN': 5,
                    'MAX': {
                        'VALUE': 9,
                        'EXCLUSIVE': False
                    }
                },
                'tree.height': 9
            },
            mapping=search.DEFAULT_MAPPING)

        p1 = ('AND', {('width__lte', 9), ('width__gte', 5),
                      ('tree__height', 9)})

        self.assertEqual(self.destructure_query_set(pred), p1)

        pred = search._parse_predicate(
            {
                'tree.leaf_type': {
                    'IS': 9
                },
                'tree.last_updated_by': 4
            },
            mapping=search.DEFAULT_MAPPING)

        p2 = ('AND', {('tree__leaf_type', 9), ('tree__last_updated_by', 4)})

        self.assertEqual(self.destructure_query_set(pred), p2)

    def test_parse_predicate_with_tree_map(self):
        pred = search._parse_predicate(
            {
                'plot.width': {
                    'MIN': 5,
                    'MAX': {
                        'VALUE': 9,
                        'EXCLUSIVE': False
                    }
                },
                'tree.height': 9
            },
            mapping=search.TREE_MAPPING)

        p1 = ('AND', {('plot__width__lte', 9), ('plot__width__gte', 5),
                      ('height', 9)})

        self.assertEqual(self.destructure_query_set(pred), p1)

    def test_parse_filter_no_wrapper(self):
        pred = search._parse_filter(
            {
                'plot.width': {
                    'MIN': 5,
                    'MAX': {
                        'VALUE': 9,
                        'EXCLUSIVE': False
                    }
                },
                'tree.height': 9
            },
            mapping=search.DEFAULT_MAPPING)

        p = ('AND', {('width__lte', 9), ('width__gte', 5),
                     ('tree__height', 9)})

        self.assertEqual(self.destructure_query_set(pred), p)

    def test_parse_filter_and(self):
        pred = search._parse_filter([
            'AND', {
                'plot.width': {
                    'MIN': 5,
                    'MAX': {
                        'VALUE': 9,
                        'EXCLUSIVE': False
                    }
                },
                'tree.height': 9
            }, {
                'tree.leaf_type': {
                    'IS': 9
                },
                'tree.last_updated_by': 4
            }
        ],
                                    mapping=search.DEFAULT_MAPPING)

        p = ('AND', {('width__lte', 9), ('width__gte', 5), ('tree__height', 9),
                     ('tree__leaf_type', 9), ('tree__last_updated_by', 4)})

        self.assertEqual(self.destructure_query_set(pred), p)

    def test_parse_filter_or(self):
        pred = search._parse_filter([
            'OR', {
                'plot.width': {
                    'MIN': 5,
                    'MAX': {
                        'VALUE': 9,
                        'EXCLUSIVE': False
                    }
                },
                'tree.height': 9
            }, {
                'tree.leaf_type': {
                    'IS': 9
                },
                'tree.last_updated_by': 4
            }
        ],
                                    mapping=search.DEFAULT_MAPPING)

        p1 = ('AND',
              frozenset({('width__lte', 9), ('width__gte', 5),
                         ('tree__height', 9)}))

        p2 = ('AND',
              frozenset({('tree__leaf_type', 9),
                         ('tree__last_updated_by', 4)}))

        self.assertEqual(self.destructure_query_set(pred), ('OR', {p1, p2}))

    def test_parse_collection_udf_simple_predicate(self):
        self._setup_tree_and_collection_udf()
        pred = search._parse_predicate(
            {'udf:plot:%s.action' % self.plotstew.pk: 'prune'},
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('id__in', (self.plot.pk, ))})

        self.assertEqual(self.destructure_query_set(pred), target)

    def test_parse_collection_udf_fail_nondate_comparison(self):
        self._setup_tree_and_collection_udf()

        with self.assertRaises(search.ParseException):
            search._parse_predicate(
                {'udf:tree:%s.date' % self.treestew.pk: {
                    'MAX': 3
                }},
                mapping=search.DEFAULT_MAPPING)

    def test_parse_collection_udf_nested_pass_date_comparison(self):
        self._setup_tree_and_collection_udf()

        pred = search._parse_predicate(
            {
                'udf:tree:%s.date' % self.treestew.pk: {
                    'MAX': '2014-05-01 00:00:00'
                }
            },
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('tree__id__in', (self.tree.pk, ))})

        self.assertEqual(self.destructure_query_set(pred), target)

    def test_parse_normal_value(self):
        self.assertEqual(search._parse_value(1), 1)

    def test_parse_list(self):
        self.assertEqual(search._parse_value([1, 2]), [1, 2])

    def test_parse_date(self):
        date = datetime(2013, 4, 1, 12, 0, 0)
        self.assertEqual(search._parse_value("2013-04-01 12:00:00"), date)
Example #33
0
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 split_model_or_raise(identifier):
        parts = identifier.split('.', 1)

        if (len(parts) != 2 or
                parts[0] not in feature_object_names + ['tree']):
            raise Exception(
                'Malformed request - invalid field %s' % identifier)
        else:
            return parts

    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_validation_errors(thing._model_name, e)

    tree = None

    for (identifier, value) in request_dict.iteritems():
        object_name, field = split_model_or_raise(identifier)

        if 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.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)

    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:
        raise ValidationError(errors)

    # Refresh feature.instance in case geo_rev_hash was updated
    feature.instance = Instance.objects.get(id=feature.instance.id)

    return feature, tree
Example #34
0
def get_audits(logged_in_user,
               instance,
               query_vars,
               user,
               models,
               model_id,
               page=0,
               page_size=20,
               exclude_pending=True,
               should_count=False):
    start_pos = page * page_size
    end_pos = start_pos + page_size

    if instance:
        if instance.is_accessible_by(logged_in_user):
            instances = Instance.objects.filter(pk=instance.pk)
        else:
            instances = Instance.objects.none()
    # If we didn't specify an instance we only want to
    # show audits where the user has permission
    else:
        instances = Instance.objects\
                            .filter(pk__in=_instance_ids_edited_by(user))\
                            .filter(user_accessible_instance_filter(
                                logged_in_user))\
                            .distinct()

    if not instances.exists():
        # Force no results
        return {
            'audits': Audit.objects.none(),
            'total_count': 0,
            'next_page': None,
            'prev_page': None
        }

    map_feature_models = set(MapFeature.subclass_dict().keys())
    model_filter = Q()
    # We only want to show the TreePhoto's image, not other fields
    # and we want to do it automatically if 'Tree' was specified as
    # a model.  The same goes for MapFeature(s) <-> MapFeaturePhoto
    # There is no need to check permissions, because photos are always visible
    if 'Tree' in models:
        model_filter = model_filter | Q(model='TreePhoto', field='image')
    if map_feature_models.intersection(models):
        model_filter = model_filter | Q(model='MapFeaturePhoto', field='image')

    if logged_in_user == user:
        # The logged-in user can see all their own edits
        model_filter = model_filter | \
            Q(model__in=models) | Q(model__startswith='udf:')
    else:
        # Filter other users' edits by their visibility to the logged-in user
        for inst in instances:
            for model in models:
                ModelClass = get_auditable_class(model)
                if issubclass(ModelClass, Authorizable):
                    fake_model = ModelClass(instance=inst)
                    visible_fields = fake_model.visible_fields(logged_in_user)
                    model_filter = model_filter |\
                        Q(model=model, field__in=visible_fields, instance=inst)
                else:
                    model_filter = model_filter | Q(model=model, instance=inst)

                # Add UDF collections related to model
                if model == 'Tree':
                    fake_model = Tree(instance=inst)
                elif model == 'Plot':
                    fake_model = Plot(instance=inst)
                else:
                    continue

                model_collection_udfs_audit_names =\
                    fake_model.visible_collection_udfs_audit_names(
                        logged_in_user)

                model_filter = model_filter |\
                    Q(model__in=model_collection_udfs_audit_names)

    udf_bookkeeping_fields = Q(model__startswith='udf:',
                               field__in=('id', 'model_id',
                                          'field_definition'))

    audits = (Audit.objects.filter(model_filter).filter(
        instance__in=instances).select_related('instance').exclude(
            udf_bookkeeping_fields).exclude(
                user=User.system_user()).order_by('-created'))

    if user:
        audits = audits.filter(user=user)
    if model_id:
        audits = audits.filter(model_id=model_id)
    if exclude_pending:
        audits = audits.exclude(requires_auth=True, ref__isnull=True)

    total_count = audits.count() if should_count else 0
    audits = audits[start_pos:end_pos]

    query_vars = {k: v for (k, v) in query_vars.iteritems() if k != 'page'}
    next_page = None
    prev_page = None
    # We are using len(audits) instead of audits.count() because we
    # have already realized the queryset at this point
    if len(audits) == page_size:
        query_vars['page'] = page + 1
        next_page = "?" + urllib.urlencode(query_vars)
    if page > 0:
        query_vars['page'] = page - 1
        prev_page = "?" + urllib.urlencode(query_vars)

    return {
        'audits': audits,
        'total_count': total_count,
        'next_page': next_page,
        'prev_page': prev_page
    }
Example #35
0
 def test_delete_tree(self):
     tree = Tree(instance=self.instance, plot=self.plot)
     tree.save_with_user(self.user)
     self.go_to_feature_detail(self.plot.pk)
     self._execute_delete_workflow((1, 1), (1, 0))
Example #36
0
    def test_plot_history_shows_all_trees(self):
        p = Plot(instance=self.instance, geom=self.p)
        p.save_with_user(self.user)

        self.assertEqual(len(p.get_tree_history()), 0)

        t = Tree(plot=p, instance=self.instance)
        t.save_with_user(self.user)
        tpk = t.pk

        self.assertEqual(list(p.get_tree_history()), [tpk])

        t.delete_with_user(self.user)

        self.assertEqual(list(p.get_tree_history()), [tpk])

        t2 = Tree(plot=p, instance=self.instance)
        t2.save_with_user(self.user)

        self.assertEqual(list(p.get_tree_history()), [t2.pk, tpk])

        t3 = Tree(plot=p, instance=self.instance)
        t3.save_with_user(self.user)

        self.assertEqual(list(p.get_tree_history()), [t3.pk, t2.pk, tpk])
Example #37
0
    def test_plot_tree_same_instance(self):
        plot = Plot(geom=self.p1, instance=self.instance2)
        plot.save_with_user(self.user)

        tree = Tree(plot=plot, instance=self.instance1, readonly=False)
        self.assertRaises(ValidationError, tree.save_with_user, self.user)
Example #38
0
 def test_update_diameter(self):
     with self._assert_updates_eco_rev(True):
         tree = Tree(instance=self.instance, plot=self.plot, diameter=3)
         tree.save_with_user(self.user)
         request_dict = {'tree.diameter': '5'}
         update_map_feature(request_dict, self.user, self.plot)
Example #39
0
def get_audits(logged_in_user,
               instance,
               query_vars,
               user,
               models,
               model_id,
               page=0,
               page_size=20,
               exclude_pending=True,
               should_count=False):
    start_pos = page * page_size
    end_pos = start_pos + page_size

    model_filter = Q(model__in=models)

    # We only want to show the TreePhoto's image, not other fields
    # and we want to do it automatically if 'Tree' was specified as
    # a model
    # FIXME: This should also show MapFeaturePhoto if any map feature
    #        models are in the filter
    if 'Tree' in models:
        model_filter = model_filter | Q(model='TreePhoto', field='image')

    if instance:
        if instance.is_accessible_by(logged_in_user):
            instances = Instance.objects.filter(pk=instance.pk)
        else:
            instances = []
    # If we didn't specify an instance we only want to
    # show audits where the user has permission
    else:
        instances = Instance.objects.filter(
            _user_accessible_instance_filter(logged_in_user))

    if len(instances) == 0:
        # Force no results
        return {
            'audits': [],
            'total_count': 0,
            'next_page': None,
            'prev_page': None
        }

    map_feature_models = set(MapFeature.subclass_dict().keys())
    model_filter = Q()
    # We only want to show the TreePhoto's image, not other fields
    # and we want to do it automatically if 'Tree' was specified as
    # a model.  The same goes for MapFeature(s) <-> MapFeaturePhoto
    # There is no need to check permissions, because photos are always visible
    if 'Tree' in models:
        model_filter = model_filter | Q(model='TreePhoto', field='image')
    if map_feature_models.intersection(models):
        model_filter = model_filter | Q(model='MapFeaturePhoto', field='image')

    # We need a filter per-instance in to only show fields visible to the user
    for inst in instances:
        for model in models:
            ModelClass = get_auditable_class(model)
            if issubclass(ModelClass, Authorizable):
                fake_model = ModelClass(instance=inst)
                visible_fields = fake_model.visible_fields(logged_in_user)
                model_filter = model_filter |\
                    Q(model=model, field__in=visible_fields, instance=inst)
            else:
                model_filter = model_filter | Q(model=model, instance=inst)

            # Only add UDF collections if their parent models are being shown
            if model == 'Tree':
                fake_model = Tree(instance=inst)
            elif model == 'Plot':
                fake_model = Plot(instance=inst)
            else:
                continue

            model_collection_udfs_audit_names =\
                fake_model.visible_collection_udfs_audit_names(logged_in_user)

            # Don't show the fields that every collection UDF has, because they
            # are not very interesting
            model_filter = model_filter |\
                (Q(model__in=model_collection_udfs_audit_names) &
                 ~Q(field__in=('id', 'model_id', 'field_definition')))

    audits = Audit.objects.filter(model_filter)\
                          .filter(instance__in=instances)\
                          .order_by('-created', 'id')

    if user:
        audits = audits.filter(user=user)
    if model_id:
        audits = audits.filter(model_id=model_id)
    if exclude_pending:
        audits = audits.exclude(requires_auth=True, ref__isnull=True)

    total_count = audits.count() if should_count else 0
    audits = audits[start_pos:end_pos]

    query_vars = {k: v for (k, v) in query_vars.iteritems() if k != 'page'}
    next_page = None
    prev_page = None
    if len(audits) == page_size:
        query_vars['page'] = page + 1
        next_page = "?" + urllib.urlencode(query_vars)
    if page > 0:
        query_vars['page'] = page - 1
        prev_page = "?" + urllib.urlencode(query_vars)

    return {
        'audits': audits,
        'total_count': total_count,
        'next_page': next_page,
        'prev_page': prev_page
    }
 def test_display_value_converts_model_name(self):
     self.assertEqual('Tree', display_name(Tree()))
Example #41
0
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)

    old_location = feature.geom

    tree = None

    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.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.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)

    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_location is None or not feature.geom.equals_exact(old_location):
        feature.instance.update_geo_rev()

    return feature, tree
    def test_tree_overrides_plot(self):
        tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
        tree.save_with_user(self.other)

        self.clear_and_set_and_reload()
        self.assertEqual(self.plot.updated_by_id, self.other.pk)
Example #43
0
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 value_is_redundant(model, field_name, value):
        # The iOS app sends a key in `data` for every udf definition,
        # even if it hasn't changed.
        # If it is trying to delete a custom field that is not defined
        # for the model, flag it as redundant,
        # to avoid a `KeyError` when the update tries to delete them.
        if field_name.startswith('udf:') and \
                value in [[], '[]', '', None]:
            udf_name = field_name.replace('udf:', '')
            if udf_name not in model.udfs:
                return True
        return False

    def set_attr_on_model(model, attr, val):
        field_classname = \
            model._meta.get_field(attr).__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)

    def skip_setting_value_on_tree(value, tree):
        # If the tree is not None, we always set a value.  If the tree
        # is None (meaning that we would be creating a new Tree
        # object) then we only want to set a value if the value is
        # non-empty.
        return (tree is None) and (value in ([], '[]', '', None))

    tree = None
    errors = {}

    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 ValueError(split_template % identifier)

        if (object_name == 'tree'
            and skip_setting_value_on_tree(
                value, feature.safe_get_current_tree())):
            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 ValueError(
                'Malformed request - invalid model %s' % object_name)

        if not value_is_redundant(model, field, value):
            set_attr_on_model(model, field, value)

        field_class = model._meta.get_field(field)
        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')

    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
Example #44
0
    def test_basic_audit(self):
        plot = Plot(geom=self.instance.center, instance=self.instance)
        plot.save_with_user(self.user1)

        self.assertAuditsEqual([
            self.make_audit(plot.pk, 'id', None, str(plot.pk), model='Plot'),
            self.make_audit(plot.pk, 'readonly', None, 'False',
                            model='Plot'),
            self.make_audit(plot.pk, 'geom', None, str(plot.geom),
                            model='Plot')], plot.audits())

        t = Tree(plot=plot, instance=self.instance, readonly=True)

        t.save_with_user(self.user1)

        expected_audits = [
            self.make_audit(t.pk, 'id', None, str(t.pk)),
            self.make_audit(t.pk, 'readonly', None, True),
            self.make_audit(t.pk, 'plot', None, plot.pk)]

        self.assertAuditsEqual(expected_audits, t.audits())

        t.readonly = False
        t.save_with_user(self.user2)

        expected_audits.insert(
            0, self.make_audit(t.pk, 'readonly', 'True', 'False',
                               action=Audit.Type.Update, user=self.user2))

        self.assertAuditsEqual(expected_audits, t.audits())

        old_pk = t.pk
        t.delete_with_user(self.user1)

        expected_audits.insert(
            0, self.make_audit(old_pk, None, None, None,
                               action=Audit.Type.Delete, user=self.user1))

        self.assertAuditsEqual(
            expected_audits,
            Audit.audits_for_model('Tree', self.instance, old_pk))
Example #45
0
class ModelUnicodeTests(TestCase):

    def setUp(self):
        self.instance = make_instance(name='Test Instance')

        self.species = Species(instance=self.instance,
                               common_name='Test Common Name',
                               genus='Test Genus',
                               cultivar='Test Cultivar',
                               species='Test Species')
        self.species.save_base()

        self.user = make_user(username='******', password='******')

        self.plot = Plot(geom=Point(0, 0), instance=self.instance,
                         address_street="123 Main Street")

        self.plot.save_base()

        self.tree = Tree(plot=self.plot, instance=self.instance)

        self.tree.save_base()

        self.boundary = make_simple_boundary("Test Boundary")

        self.role = Role(instance=self.instance, name='Test Role',
                         rep_thresh=2)
        self.role.save()

        self.field_permission = FieldPermission(
            model_name="Tree",
            field_name="readonly",
            permission_level=FieldPermission.READ_ONLY,
            role=self.role,
            instance=self.instance)
        self.field_permission.save_base()

        self.audit = Audit(action=Audit.Type.Update,
                           model="Tree",
                           field="readonly",
                           model_id=1,
                           user=self.user,
                           previous_value=True,
                           current_value=False)
        self.audit.save_base()

        self.reputation_metric = ReputationMetric(instance=self.instance,
                                                  model_name="Tree",
                                                  action="Test Action")
        self.reputation_metric.save_base()

    def test_instance_model(self):
        self.assertEqual(unicode(self.instance), "Test Instance")

    def test_species_model(self):
        self.assertEqual(
            unicode(self.species),
            "Test Common Name [Test Genus Test Species 'Test Cultivar']")

    def test_user_model(self):
        self.assertEqual(unicode(self.user), 'commander')

    def test_plot_model(self):
        self.assertEqual(unicode(self.plot),
                         'Plot (0.0, 0.0) 123 Main Street')

    def test_tree_model(self):
        self.assertEqual(unicode(self.tree), '')

    def test_boundary_model(self):
        self.assertEqual(unicode(self.boundary), 'Test Boundary')

    def test_role_model(self):
        self.assertEqual(unicode(self.role), 'Test Role (%s)' % self.role.pk)

    def test_field_permission_model(self):
        self.assertEqual(unicode(self.field_permission),
                         'Tree.readonly - Test Role (%s)' % self.role.pk)

    def test_audit_model(self):
        self.assertEqual(
            unicode(self.audit),
            'pk=%s - action=Update - Tree.readonly:(1) - True => False'
            % self.audit.pk)

    def test_reputation_metric_model(self):
        self.assertEqual(unicode(self.reputation_metric),
                         'Test Instance - Tree - Test Action')
Example #46
0
    def test_lots_of_trees_and_plots(self):
        """
        Make 3 plots: 2 pending and 1 approved
        Make 4 trees: 1 on each pending plot, 2 on approved plot
        Approve one pending plot.
        Approve all trees. The one on the (Still) pending plot
        should fail. all else should pass.
        """
        plot1 = Plot(geom=self.instance.center, instance=self.instance)
        plot2 = Plot(geom=self.instance.center, instance=self.instance)
        plot3 = Plot(geom=self.instance.center, instance=self.instance)
        plot1.save_with_user(self.commander_user)
        plot2.save_with_user(self.pending_user)
        plot3.save_with_user(self.pending_user)
        tree1 = Tree(plot=plot1, instance=self.instance)
        tree1.save_with_user(self.pending_user)
        tree2 = Tree(plot=plot1, instance=self.instance)
        tree2.save_with_user(self.pending_user)
        tree3 = Tree(plot=plot2, instance=self.instance)
        tree3.save_with_user(self.pending_user)
        tree4 = Tree(plot=plot3, instance=self.instance)
        tree4.save_with_user(self.pending_user)

        approve_or_reject_audits_and_apply(
            plot2.audits(),
            self.commander_user, True)

        approve_or_reject_audits_and_apply(
            tree1.audits(),
            self.commander_user, True)

        approve_or_reject_audits_and_apply(
            tree2.audits(),
            self.commander_user, True)

        approve_or_reject_audits_and_apply(
            tree3.audits(),
            self.commander_user, True)

        self.assertRaises(ObjectDoesNotExist, Plot.objects.get, pk=plot3.pk)
        self.assertRaises(ObjectDoesNotExist,
                          approve_or_reject_audits_and_apply,
                          tree4.audits(),
                          self.commander_user, True)
Example #47
0
 def test_add_tree_sets_updated(self):
     tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
     tree.save_with_user(self.user)
     self.assertGreater(self.plot.updated_at, self.initial_updated)
Example #48
0
class UserRoleFieldPermissionTest(MultiUserTestCase):
    def setUp(self):
        super(UserRoleFieldPermissionTest, self).setUp()

        self.plot = Plot(geom=self.p1, instance=self.instance)
        self.plot.save_with_user(self.commander_user)

        self.tree = Tree(plot=self.plot, instance=self.instance)
        self.tree.save_with_user(self.direct_user)

    def test_no_permission_cant_edit_object(self):
        self.plot.length = 10
        self.assertRaises(AuthorizeException,
                          self.plot.save_with_user, self.outlaw_user)

        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).length, 10)

        self.tree.diameter = 10
        self.assertRaises(AuthorizeException,
                          self.tree.save_with_user, self.outlaw_user)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

    def test_readonly_cant_edit_object(self):
        self.plot.length = 10
        self.assertRaises(AuthorizeException,
                          self.plot.save_with_user, self.observer_user)

        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).length, 10)

        self.tree.diameter = 10
        self.assertRaises(AuthorizeException,
                          self.tree.save_with_user, self.observer_user)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

    def test_writeperm_allows_write(self):
        self.plot.length = 10
        self.plot.save_with_user(self.direct_user)
        self.assertEqual(Plot.objects.get(pk=self.plot.pk).length, 10)

        self.tree.diameter = 10
        self.tree.save_with_user(self.direct_user)
        self.assertEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

    def test_masking_authorized(self):
        "When masking with a superuser, nothing should happen"
        self.plot.width = 5
        self.plot.save_with_user(self.commander_user)

        plot = Plot.objects.get(pk=self.plot.pk)
        plot.mask_unauthorized_fields(self.commander_user)
        self.assertEqual(self.plot.width, plot.width)

    def test_masking_unauthorized(self):
        "Masking changes an unauthorized field to None"
        self.plot.width = 5
        self.plot.save_base()

        plot = Plot.objects.get(pk=self.plot.pk)
        plot.mask_unauthorized_fields(self.observer_user)
        self.assertEqual(plot.width, None)
        # geom is always readable
        self.assertEqual(plot.geom, self.plot.geom)

        plot = Plot.objects.get(pk=self.plot.pk)
        plot.mask_unauthorized_fields(self.outlaw_user)
        self.assertEqual(plot.width, None)
        # geom is always readable
        self.assertEqual(plot.geom, self.plot.geom)

    def test_write_fails_if_any_fields_cant_be_written(self):
        """ If a user tries to modify several fields simultaneously,
        only some of which s/he has access to, the write will fail
        for all fields."""
        self.plot.length = 10
        self.plot.width = 110

        self.assertRaises(AuthorizeException,
                          self.plot.save_with_user, self.direct_user)

        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).length, 10)
        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).width, 110)

        self.tree.diameter = 10
        self.tree.canopy_height = 110

        self.assertRaises(AuthorizeException, self.tree.save_with_user,
                          self.direct_user)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).diameter,
                            10)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).canopy_height,
                            110)
Example #49
0
    def handle_row(self, row):
        self.log_verbose(row)

        # check the physical location
        ok, x, y = self.check_coords(row)
        if not ok: return

        plot = Plot()

        try:
            if self.base_srid != 4326:
                geom = Point(x, y, srid=self.base_srid)
                geom.transform(self.tf)
                self.log_verbose(geom)
                plot.geometry = geom
            else:
                plot.geometry = Point(x, y, srid=4326)
        except:
            self.log_error("ERROR: Geometry failed to transform", row)
            return

        # check the species (if any)
        ok, species = self.check_species(row)
        if not ok: return

        # check for tree info, should we create a tree or just a plot
        if species or self.check_tree_info(row):
            tree = Tree(plot=plot)
        else:
            tree = None

        if tree and species:
            tree.species = species[0]

        # check the proximity (try to match up with existing trees)
        # this may return a different plot/tree than created just above,
        # so don't set anything else on either until after this point
        ok, plot, tree = self.check_proximity(plot, tree, species, row)
        if not ok: return

        if row.get('ADDRESS') and not plot.address_street:
            plot.address_street = str(row['ADDRESS']).title()
            plot.geocoded_address = str(row['ADDRESS']).title()

        if not plot.geocoded_address:
            plot.geocoded_address = ""

        # FIXME: get this from the config?
        plot.address_state = 'CA'
        plot.import_event = self.import_event
        plot.last_updated_by = self.updater
        plot.data_owner = self.data_owner
        plot.readonly = self.readonly

        if row.get('PLOTTYPE'):
            for k, v in choices['plot_types']:
                if v == row['PLOTTYPE']:
                    plot.type = k
                    break

        if row.get('PLOTLENGTH'):
            plot.length = row['PLOTLENGTH']

        if row.get('PLOTWIDTH'):
            plot.width = row['PLOTWIDTH']

        if row.get('ID'):
            plot.owner_orig_id = row['ID']

        if row.get('ORIGID'):
            plot.owner_additional_properties = "ORIGID=" + str(row['ORIGID'])

        if row.get('OWNER_ADDITIONAL_PROPERTIES'):
            plot.owner_additional_properties = str(
                plot.owner_additional_properties) + " " + str(
                    row['OWNER_ADDITIONAL_PROPERTIES'])

        if row.get('OWNER_ADDITIONAL_ID'):
            plot.owner_additional_id = str(row['OWNER_ADDITIONAL_ID'])

        if row.get('POWERLINE'):
            for k, v in choices['powerlines']:
                if v == row['POWERLINE']:
                    plot.powerline = k
                    break

        sidewalk_damage = row.get('SIDEWALK')
        if sidewalk_damage is None or sidewalk_damage.strip() == "":
            pass
        elif sidewalk_damage is True or sidewalk_damage.lower(
        ) == "true" or sidewalk_damage.lower() == 'yes':
            plot.sidewalk_damage = 2
        else:
            plot.sidewalk_damage = 1

        plot.quick_save()

        pnt = plot.geometry
        n = Neighborhood.objects.filter(geometry__contains=pnt)
        z = ZipCode.objects.filter(geometry__contains=pnt)

        plot.neighborhoods = ""
        plot.neighborhood.clear()
        plot.zipcode = None

        if n:
            for nhood in n:
                if nhood:
                    plot.neighborhoods = plot.neighborhoods + " " + nhood.id.__str__(
                    )
                    plot.neighborhood.add(nhood)

        if z: plot.zipcode = z[0]

        plot.quick_save()

        if tree:
            tree.plot = plot
            tree.readonly = self.readonly
            tree.import_event = self.import_event
            tree.last_updated_by = self.updater

            if row.get('OWNER'):
                tree.tree_owner = str(row["OWNER"])

            if row.get('STEWARD'):
                tree.steward_name = str(row["STEWARD"])

            if row.get('SPONSOR'):
                tree.sponsor = str(row["SPONSOR"])

            if row.get('DATEPLANTED'):
                date_string = str(row['DATEPLANTED'])
                try:
                    date = datetime.strptime(date_string, "%m/%d/%Y")
                except:
                    pass
                try:
                    date = datetime.strptime(date_string, "%Y/%m/%d")
                except:
                    pass
                if not date:
                    raise ValueError(
                        "Date strings must be in mm/dd/yyyy or yyyy/mm/dd format"
                    )

                tree.date_planted = date.strftime("%Y-%m-%d")

            if row.get('DIAMETER'):
                tree.dbh = float(row['DIAMETER'])

            if row.get('HEIGHT'):
                tree.height = float(row['HEIGHT'])

            if row.get('CANOPYHEIGHT'):
                tree.canopy_height = float(row['CANOPYHEIGHT'])

            if row.get('CONDITION'):
                for k, v in choices['conditions']:
                    if v == row['CONDITION']:
                        tree.condition = k
                        break

            if row.get('CANOPYCONDITION'):
                for k, v in choices['canopy_conditions']:
                    if v == row['CANOPYCONDITION']:
                        tree.canopy_condition = k
                        break
            # FOR OTM INDIA
            # GIRTH_CM", "GIRTH_M", "HEIGHT_FT", "HEIGHT_M", "NEST", "BURROWS", "FLOWERS", "FRUITS", "NAILS", "POSTER", "WIRES", "TREE_GUARD", "NUISANCE", "NUISANCE_DESC", "HEALTH_OF_TREE", "FOUND_ON_GROUND", "GROUND_DESCRIPTION", "RISK_ON_TREE", "RISK_DESC", "RARE", "ENDANGERED", "VULNERABLE", "PEST_AFFECTED", "REFER_TO_DEPT", "SPECIAL_OTHER", "SPECIAL_OTHER_DESCRIPTION", "LATITUDE", "LONGITUDE", "CREATION_DATE", "DEVICE_ID", "TIME", "DATE"])

            if row.get('GIRTH_M'):
                tree.girth_m = float(row['GIRTH_M'])

            if row.get('HEIGHT_M'):
                tree.height_m = float(row['HEIGHT_M'])

            if row.get('NEST'):
                tree.nest = str(row['NEST'])

            if row.get('BURROWS'):
                tree.burrows = str(row['BURROWS'])

            if row.get('FLOWERS'):
                tree.flowers = str(row['FLOWERS'])

            if row.get('FRUITS'):
                tree.fruits = str(row['FRUITS'])

            if row.get('NAILS'):
                tree.nails = str(row['NAILS'])

            if row.get('POSTER'):
                tree.poster = str(row['POSTER'])

            if row.get('WIRES'):
                tree.wires = str(row['WIRES'])

            if row.get('TREE_GUARD'):
                tree.tree_guard = str(row['TREE_GUARD'])

            if row.get('NUISANCE'):
                tree.other_nuisance = bool(row['NUISANCE'])

            if row.get('NUISANCE_DESC'):
                tree.other_nuisance_desc = str(row['NUISANCE_DESC'])

            if row.get('HEALTH_OF_TREE'):
                tree.health_of_tree = str(row['HEALTH_OF_TREE'])

            if row.get('FOUND_ON_GROUND'):
                tree.found_on_ground = str(row['FOUND_ON_GROUND'])

            if row.get('GROUND_DESCRIPTION'):
                tree.ground_description = str(row['GROUND_DESCRIPTION'])

            if row.get('RISK_ON_TREE'):
                tree.risk_on_tree = str(row['RISK_ON_TREE'])

            if row.get('RISK_DESC'):
                tree.risk_desc = str(row['RISK_DESC'])

            if row.get('PEST_AFFECTED'):
                tree.pests = str(row['PEST_AFFECTED'])

            if row.get('REFER_TO_DEPT'):
                tree.refer_to_dept = str(row['REFER_TO_DEPT'])

            if row.get('SPECIAL_OTHER'):
                tree.special_other = str(row['SPECIAL_OTHER'])

            if row.get('SPECIAL_OTHER_DESCRIPTION'):
                tree.special_other_description = str(
                    row['SPECIAL_OTHER_DESCRIPTION'])

            if row.get('LATITUDE'):
                tree.latitude = str(row['LATITUDE'])

            if row.get('LONGITUDE'):
                tree.longitude = str(row['LONGITUDE'])

            if row.get('PRABHAG_ID'):
                tree.prabhag_id = str(row['PRABHAG_ID'])

            if row.get('CLUSTER_ID'):
                tree.cluster_id = str(row['CLUSTER_ID'])

            #if row.get('ID'):
            #    tree.id = str(row['ID'])

            #import pdb; pdb.set_trace()

            f = open("trees_in_otm_obj.log", "w")
            f.write("b4 save")
            f.write(str(tree.__dict__))
            tree.quick_save()
            f.write("after save \n")
            f.write(str(tree.__dict__))
            f.close()

            if row.get('PROJECT_1'):
                for k, v in Choices().get_field_choices('local'):
                    if v == row['PROJECT_1']:
                        local = TreeFlags(key=k,
                                          tree=tree,
                                          reported_by=self.updater)
                        local.save()
                        break
            if row.get('PROJECT_2'):
                for k, v in Choices().get_field_choices('local'):
                    if v == row['PROJECT_2']:
                        local = TreeFlags(key=k,
                                          tree=tree,
                                          reported_by=self.updater)
                        local.save()
                        break
            if row.get('PROJECT_3'):
                for k, v in Choices().get_field_choices('local'):
                    if v == row['PROJECT_3']:
                        local = TreeFlags(key=k,
                                          tree=tree,
                                          reported_by=self.updater)
                        local.save()
                        break

            # rerun validation tests and store results
            tree.validate_all()
Example #50
0
class UserRoleModelPermissionTest(MultiUserTestCase):
    def setUp(self):
        super(UserRoleModelPermissionTest, self).setUp()

        self.plot = Plot(geom=self.p1, instance=self.instance)
        self.plot.save_with_user(self.direct_user)

        self.tree = Tree(plot=self.plot, instance=self.instance)
        self.tree.save_with_user(self.direct_user)

    def _change_user_role(self, user, role):
        iuser = user.get_instance_user(self.instance)
        iuser.role = role
        iuser.save_with_user(self.commander_user)

    def test_save_new_object_authorized_officer(self):
        ''' Save two new objects with authorized user,
        nothing should happen'''
        plot = Plot(geom=self.p1, instance=self.instance)

        plot.save_with_user(self.direct_user)

        tree = Tree(plot=plot, instance=self.instance)

        tree.save_with_user(self.direct_user)

    def test_save_new_object_authorized_conjurer(self):
        ''' Save two new objects with authorized user,
        nothing should happen'''
        plot = Plot(geom=self.p1, instance=self.instance)

        plot.save_with_user(self.conjurer_user)

        tree = Tree(plot=plot, instance=self.instance)

        tree.save_with_user(self.conjurer_user)

    def test_save_new_object_unauthorized_outlaw(self):
        plot = Plot(geom=self.p1, instance=self.instance)

        self.assertRaises(AuthorizeException,
                          plot.save_with_user, self.outlaw_user)

        plot.save_base()
        tree = Tree(plot=plot, instance=self.instance)

        self.assertRaises(AuthorizeException,
                          tree.save_with_user, self.outlaw_user)

    def test_save_new_object_unauthorized_tweaker(self):
        plot = Plot(geom=self.p1, instance=self.instance)

        self.assertRaises(AuthorizeException,
                          plot.save_with_user, self.tweaker_user)

        plot.save_base()
        tree = Tree(plot=plot, instance=self.instance)

        self.assertRaises(AuthorizeException,
                          tree.save_with_user, self.tweaker_user)

    def test_assign_commander_role_can_delete(self):
        with self.assertRaises(AuthorizeException):
            self.tree.delete_with_user(self.outlaw_user)

        self._change_user_role(
            self.outlaw_user, make_commander_role(self.tree.get_instance()))

        self.tree.delete_with_user(self.outlaw_user)
        self.assertEqual(Tree.objects.count(), 0)

    def test_delete_object(self):
        with self.assertRaises(AuthorizeException):
            self.tree.delete_with_user(self.outlaw_user)
        self.tree.delete_with_user(self.commander_user)

        with self.assertRaises(AuthorizeException):
            self.plot.delete_with_user(self.outlaw_user, cascade=True)
        self.plot.delete_with_user(self.commander_user, cascade=True)

    def test_delete_object_you_created(self):
        outlaw_role = self.outlaw_user.get_role(self.instance)
        self._change_user_role(self.direct_user, outlaw_role)
        self.tree.delete_with_user(self.direct_user)
        self.plot.delete_with_user(self.direct_user, cascade=True)
Example #51
0
class FilterParserCollectionTests(OTMTestCase):
    def _setup_tree_and_collection_udf(self):
        instance = self.instance = make_instance()
        commander = self.commander = make_commander_user(instance)

        self.plotstew, self.treestew = \
            _setup_collection_udfs(instance, commander)

        d1 = {'action': 'prune', 'date': "2014-05-3 00:00:00"}
        d2 = {'action': 'water', 'date': "2014-04-29 00:00:00"}

        self.plot = Plot(instance=instance, geom=instance.center)
        self.plot.udfs[self.plotstew.name] = [d1]
        self.plot.save_with_user(commander)

        self.tree = Tree(instance=instance, plot=self.plot)
        self.tree.udfs[self.treestew.name] = [d2]
        self.tree.save_with_user(commander)

    def test_key_parser_tree_collection_udf(self):
        # UDF searches go on the specified model's id
        match = search._parse_predicate_key('udf:tree:52.action',
                                            mapping=search.DEFAULT_MAPPING)
        self.assertEqual(match, ('udf:tree:52', 'tree__', 'action'))

    def test_key_parser_plot_collection_udf(self):
        # UDF searches go on the specified model's id
        match = search._parse_predicate_key('udf:plot:52.action',
                                            mapping=search.DEFAULT_MAPPING)
        self.assertEqual(match, ('udf:plot:52', '', 'action'))

    def test_parse_collection_udf_simple_predicate(self):
        self._setup_tree_and_collection_udf()
        pred = search._parse_query_dict(
            {'udf:plot:%s.action' % self.plotstew.pk: 'prune'},
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('id__in', (self.plot.pk, ))})

        self.assertEqual(destructure_query_set(pred), target)

    def test_parse_collection_udf_fail_nondatenumeric_comparison(self):
        self._setup_tree_and_collection_udf()

        with self.assertRaises(search.ParseException):
            search._parse_query_dict(
                {'udf:tree:%s.date' % self.treestew.pk: {
                    'MAX': "foo"
                }},
                mapping=search.DEFAULT_MAPPING)

    def test_parse_collection_udf_nested_pass_numeric_comparison(self):
        self._setup_tree_and_collection_udf()
        agility = make_collection_udf(self.instance,
                                      model='Tree',
                                      name='Agility',
                                      datatype=[{
                                          'type': 'float',
                                          'name': 'current'
                                      }])
        set_write_permissions(self.instance, self.commander, 'Tree',
                              ['udf:Agility'])
        new_tree = Tree(instance=self.instance, plot=self.plot)
        new_tree.udfs[agility.name] = [{'current': '1.5'}]
        new_tree.save_with_user(self.commander)

        pred = search._parse_query_dict(
            {'udf:tree:%s.current' % agility.pk: {
                'MIN': 1
            }},
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('tree__id__in', (new_tree.pk, ))})

        self.assertEqual(destructure_query_set(pred), target)

    def test_parse_collection_udf_nested_pass_date_comparison(self):
        self._setup_tree_and_collection_udf()

        pred = search._parse_query_dict(
            {
                'udf:tree:%s.date' % self.treestew.pk: {
                    'MAX': '2014-05-01 00:00:00'
                }
            },
            mapping=search.DEFAULT_MAPPING)

        target = ('AND', {('tree__id__in', (self.tree.pk, ))})

        self.assertEqual(destructure_query_set(pred), target)

    def test_parse_collection_udf_date_and_action_should_fail(self):
        point = Point(0, 0)
        instance = make_instance(point=point)
        commander = make_commander_user(instance)

        plotstew, treestew = _setup_collection_udfs(instance, commander)
        _setup_models_for_collections(instance, commander, point, plotstew,
                                      treestew)

        pred = search._parse_query_dict(
            {
                'udf:plot:%s.action' % plotstew.pk: {
                    'IS': 'water'
                },
                'udf:plot:%s.date' % plotstew.pk:
                # Range encompasses p1's prune but not p1's water action
                {
                    'MIN': '2013-09-01 00:00:00',
                    'MAX': '2013-10-31 00:00:00'
                }
            },
            mapping=search.DEFAULT_MAPPING)

        connector, predset = destructure_query_set(pred)

        self.assertEqual(connector, 'AND')
        target = ('id__in', tuple())
        self.assertIn(target, predset)
Example #52
0
class EcoTest(EcoTestCase):
    def setUp(self):
        super(EcoTest, self).setUp()
        p = self.instance.center
        self.plot = Plot(geom=p, instance=self.instance)

        self.plot.save_with_user(self.user)

        self.tree = Tree(plot=self.plot,
                         instance=self.instance,
                         readonly=False,
                         species=self.species,
                         diameter=1630)

        self.tree.save_with_user(self.user)

    def test_eco_benefit_sanity(self):
        rslt, basis, error = TreeBenefitsCalculator()\
            .benefits_for_object(self.instance, self.tree.plot)

        bens = rslt['plot']

        self.assert_benefit_value(bens, BenefitCategory.ENERGY, 'kwh', 1896)
        self.assert_benefit_value(bens, BenefitCategory.AIRQUALITY, 'lbs', 6)
        self.assert_benefit_value(bens, BenefitCategory.STORMWATER, 'gal',
                                  3185)
        self.assert_benefit_value(bens, BenefitCategory.CO2, 'lbs', 563)
        self.assert_benefit_value(bens, BenefitCategory.CO2STORAGE, 'lbs',
                                  6575)

    def testSearchBenefits(self):
        request = make_request(
            {'q': json.dumps({'tree.readonly': {
                'IS': False
            }})})  # all trees
        request.instance_supports_ecobenefits = self.instance\
                                                    .has_itree_region()
        result = search_tree_benefits(request, self.instance)

        benefits = result['benefits']

        self.assertTrue(len(benefits) > 0)

    def test_group_basis_empty(self):
        basis = {}
        example = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 8
            },
            'group2': {
                'n_objects_used': 10,
                'n_objects_discarded': 12
            }
        }

        _combine_benefit_basis(basis, example)
        self.assertEqual(basis, example)

    def test_group_basis_combine_new_group(self):
        # New groups are added
        basis = {'group1': {'n_objects_used': 5, 'n_objects_discarded': 8}}
        new_group = {
            'group2': {
                'n_objects_used': 13,
                'n_objects_discarded': 4
            }
        }
        target = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 8
            },
            'group2': {
                'n_objects_used': 13,
                'n_objects_discarded': 4
            }
        }
        _combine_benefit_basis(basis, new_group)
        self.assertEqual(basis, target)

    def test_group_basis_combine_existing_groups(self):
        basis = {'group1': {'n_objects_used': 5, 'n_objects_discarded': 8}}
        update_group = {
            'group1': {
                'n_objects_used': 13,
                'n_objects_discarded': 4
            }
        }
        target = {'group1': {'n_objects_used': 18, 'n_objects_discarded': 12}}
        _combine_benefit_basis(basis, update_group)
        self.assertEqual(basis, target)

    def test_combine_benefit_groups_empty(self):
        # with and without currency
        base_group = {
            'group1': {
                'benefit1': {
                    'value': 3,
                    'currency': 9,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 3,
                    'currency': 9,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        groups = {}
        _combine_grouped_benefits(groups, base_group)

        self.assertEqual(groups, base_group)

    def test_combine_benefit_groups_no_overlap(self):
        base_group = {
            'group1': {
                'benefit1': {
                    'value': 3,
                    'currency': 9,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 4,
                    'currency': 10,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        new_group = {
            'group2': {
                'benefit1': {
                    'value': 5,
                    'currency': 11,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 6,
                    'currency': 19,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        groups = {}
        _combine_grouped_benefits(groups, base_group)
        _combine_grouped_benefits(groups, new_group)

        target = {
            'group1': base_group['group1'],
            'group2': new_group['group2']
        }

        self.assertEqual(groups, target)

    def test_combine_benefit_groups_sums_benefits(self):
        base_group = {
            'group1': {
                'benefit1': {
                    'value': 3,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 4,
                    'currency': 10,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit3': {
                    'value': 32,
                    'currency': 919,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        new_group = {
            'group1': {
                'benefit1': {
                    'value': 5,
                    'currency': 11,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 7,
                    'unit': 'gal',
                    'currency': 19,
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit4': {
                    'value': 7,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }
        groups = {}
        _combine_grouped_benefits(groups, base_group)
        _combine_grouped_benefits(groups, new_group)

        target = {
            'group1': {
                'benefit1': {
                    'value': 8,
                    'currency': 11,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit2': {
                    'value': 11,
                    'currency': 29,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit3': {
                    'value': 32,
                    'currency': 919,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                },
                'benefit4': {
                    'value': 7,
                    'unit': 'gal',
                    'label': BenefitCategory.STORMWATER,
                    'unit-name': 'eco'
                }
            }
        }

        self.assertEqual(groups, target)

    def test_annotates_basis(self):
        basis = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 15
            },
            'group2': {
                'n_objects_used': 2,
                'n_objects_discarded': 18
            }
        }
        target = {
            'group1': {
                'n_objects_used': 5,
                'n_objects_discarded': 15,
                'n_total': 20,
                'n_pct_calculated': 0.25
            },
            'group2': {
                'n_objects_used': 2,
                'n_objects_discarded': 18,
                'n_total': 20,
                'n_pct_calculated': 0.1
            }
        }
        _annotate_basis_with_extra_stats(basis)

        self.assertEqual(basis, target)
Example #53
0
class UserRoleFieldPermissionTest(OTMTestCase):
    def setUp(self):
        self.p1 = Point(-8515941.0, 4953519.0)
        self.instance = make_instance(point=self.p1)
        self.commander = make_commander_user(self.instance)
        self.officer = make_officer_user(self.instance)
        self.observer = make_observer_user(self.instance)
        self.outlaw = make_user_with_default_role(self.instance, 'outlaw')

        self.plot = Plot(geom=self.p1, instance=self.instance)
        self.plot.save_with_user(self.officer)

        self.tree = Tree(plot=self.plot, instance=self.instance)
        self.tree.save_with_user(self.officer)

    def test_no_permission_cant_edit_object(self):
        self.plot.length = 10
        self.assertRaises(AuthorizeException, self.plot.save_with_user,
                          self.outlaw)

        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).length, 10)

        self.tree.diameter = 10
        self.assertRaises(AuthorizeException, self.tree.save_with_user,
                          self.outlaw)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

    def test_readonly_cant_edit_object(self):
        self.plot.length = 10
        self.assertRaises(AuthorizeException, self.plot.save_with_user,
                          self.observer)

        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).length, 10)

        self.tree.diameter = 10
        self.assertRaises(AuthorizeException, self.tree.save_with_user,
                          self.observer)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

    def test_writeperm_allows_write(self):
        self.plot.length = 10
        self.plot.save_with_user(self.officer)
        self.assertEqual(Plot.objects.get(pk=self.plot.pk).length, 10)

        self.tree.diameter = 10
        self.tree.save_with_user(self.officer)
        self.assertEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

    def test_save_new_object_authorized(self):
        '''Save two new objects with authorized user, nothing should happen'''
        plot = Plot(geom=self.p1, instance=self.instance)

        plot.save_with_user(self.officer)

        tree = Tree(plot=plot, instance=self.instance)

        tree.save_with_user(self.officer)

    def test_save_new_object_unauthorized(self):
        plot = Plot(geom=self.p1, instance=self.instance)

        self.assertRaises(AuthorizeException, plot.save_with_user, self.outlaw)

        plot.save_base()
        tree = Tree(plot=plot, instance=self.instance)

        self.assertRaises(AuthorizeException, tree.save_with_user, self.outlaw)

    def test_make_administrator_can_delete(self):
        with self.assertRaises(AuthorizeException):
            self.tree.delete_with_user(self.outlaw)

        iuser = self.outlaw.get_instance_user(self.instance)
        role = Role.objects.create(instance=self.instance,
                                   name=Role.ADMINISTRATOR,
                                   rep_thresh=0)
        iuser.role = role
        iuser.save_with_user(self.commander)

        self.tree.delete_with_user(self.outlaw)
        self.assertEqual(Tree.objects.count(), 0)

    def test_delete_object(self):
        with self.assertRaises(AuthorizeException):
            self.tree.delete_with_user(self.outlaw)

        with self.assertRaises(AuthorizeException):
            self.plot.delete_with_user(self.outlaw, cascade=True)

        with self.assertRaises(AuthorizeException):
            self.tree.delete_with_user(self.officer)

        with self.assertRaises(AuthorizeException):
            self.plot.delete_with_user(self.officer, cascade=True)

        self.tree.delete_with_user(self.commander)
        self.plot.delete_with_user(self.commander, cascade=True)

    def test_masking_authorized(self):
        "When masking with a superuser, nothing should happen"
        self.plot.width = 5
        self.plot.save_with_user(self.commander)

        plot = Plot.objects.get(pk=self.plot.pk)
        plot.mask_unauthorized_fields(self.commander)
        self.assertEqual(self.plot.width, plot.width)

    def test_masking_unauthorized(self):
        "Masking changes an unauthorized field to None"
        self.plot.width = 5
        self.plot.save_base()

        plot = Plot.objects.get(pk=self.plot.pk)
        plot.mask_unauthorized_fields(self.observer)
        self.assertEqual(None, plot.width)

        plot = Plot.objects.get(pk=self.plot.pk)
        plot.mask_unauthorized_fields(self.outlaw)
        self.assertEqual(None, plot.width)

    def test_masking_whole_queryset(self):
        "Masking also works on entire querysets"
        self.plot.width = 5
        self.plot.save_base()

        plots = Plot.objects.filter(pk=self.plot.pk)
        plot = Plot.mask_queryset(plots, self.observer)[0]
        self.assertEqual(None, plot.width)

    def test_write_fails_if_any_fields_cant_be_written(self):
        """ If a user tries to modify several fields simultaneously,
        only some of which s/he has access to, the write will fail
        for all fields."""
        self.plot.length = 10
        self.plot.width = 110

        self.assertRaises(AuthorizeException, self.plot.save_with_user,
                          self.officer)

        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).length, 10)
        self.assertNotEqual(Plot.objects.get(pk=self.plot.pk).width, 110)

        self.tree.diameter = 10
        self.tree.canopy_height = 110

        self.assertRaises(AuthorizeException, self.tree.save_with_user,
                          self.officer)

        self.assertNotEqual(Tree.objects.get(pk=self.tree.pk).diameter, 10)

        self.assertNotEqual(
            Tree.objects.get(pk=self.tree.pk).canopy_height, 110)
Example #54
0
def context_dict_for_plot(request, plot, tree_id=None, **kwargs):
    context = context_dict_for_map_feature(request, plot, **kwargs)

    instance = request.instance
    user = request.user

    if tree_id:
        tree = get_object_or_404(Tree,
                                 instance=instance,
                                 plot=plot,
                                 pk=tree_id)
    else:
        tree = plot.current_tree()

    if tree:
        tree.convert_to_display_units()

    if tree is not None:
        photos = tree.photos()
        # can't send a regular photo qs because the API will
        # serialize this to JSON, which is not supported for qs
        context['photos'] = [
            context_dict_for_photo(request, photo) for photo in photos
        ]
    else:
        photos = []

    def get_external_link_url(user, feature, tree=None):
        if not user or not feature or not feature.is_plot:
            return None
        instance = feature.instance
        external_link_config =  \
            get_attr_from_json_field(instance, 'config.externalLink') or None
        if not external_link_config or \
                not external_link_config.get('url', None) or \
                not external_link_config.get('text', None):
            return None
        role = Role.objects.get_role(instance, user)
        if not role.has_permission('view_external_link'):
            return None
        external_url = external_link_config['url']
        if not tree and -1 < external_url.find(r'#{tree.id}'):
            return None

        plot = feature.cast_to_subtype()
        substitutes = {
            'planting_site.id': str(plot.pk),
            'planting_site.custom_id': plot.owner_orig_id or '',
            'tree.id': tree and str(tree.pk) or ''
        }

        class UrlTemplate(Template):
            delimiter = '#'
            pattern = '''
            \#(?:
                (?P<escaped>\#)         |  # escape with repeated delimiter
                (?P<named>(?:{0}))      |  # "#foo" substitutes foo keyword
                {{(?P<braced>(?:{0}))}} |  # "#{{foo}}" substitutes foo keyword
                (?P<invalid>{{}})          # requires a name
            )
            '''.format(get_external_link_choice_pattern())

        return UrlTemplate(external_url).safe_substitute(substitutes)

    context['external_link'] = get_external_link_url(user, plot, tree)

    has_tree_diameter = tree is not None and tree.diameter is not None
    has_tree_species_with_code = tree is not None \
        and tree.species is not None and tree.species.otm_code is not None
    has_photo = tree is not None and len(photos) > 0

    total_progress_items = 4
    completed_progress_items = 1  # there is always a plot

    if has_tree_diameter:
        completed_progress_items += 1
    if has_tree_species_with_code:
        completed_progress_items += 1
    if has_photo:
        completed_progress_items += 1

    context['progress_percent'] = int(
        100 * (completed_progress_items / total_progress_items))

    context['progress_messages'] = []
    if not tree:
        context['progress_messages'].append(_('Add a tree'))
    if not has_tree_diameter:
        context['progress_messages'].append(_('Add the diameter'))
    if not has_tree_species_with_code:
        context['progress_messages'].append(_('Add the species'))
    if not has_photo:
        context['progress_messages'].append(_('Add a photo'))

    url_kwargs = {
        'instance_url_name': instance.url_name,
        'feature_id': plot.pk
    }
    if tree:
        url_name = 'add_photo_to_tree'
        url_kwargs = dict(url_kwargs.items() + [('tree_id', tree.pk)])
    else:
        url_name = 'add_photo_to_plot'

    context['upload_photo_endpoint'] = reverse(url_name, kwargs=url_kwargs)

    context['plot'] = plot
    context['has_tree'] = tree is not None
    # Give an empty tree when there is none in order to show tree fields easily
    context['tree'] = tree or Tree(plot=plot, instance=instance)

    context['photo_upload_share_text'] = _photo_upload_share_text(
        plot, tree is not None)

    pmfs = PolygonalMapFeature.objects.filter(
        polygon__contains=plot.geom, mapfeature_ptr__instance_id=instance.id)

    if pmfs:
        context['containing_polygonalmapfeature'] = pmfs[0].cast_to_subtype()

    audits = _plot_audits(user, instance, plot)

    _add_audits_to_context(audits, context)

    _add_share_context(context, request, photos)

    return context
Example #55
0
    def commit_row(self):
        # If this row was already commit... abort
        if self.plot:
            self.status = TreeImportRow.SUCCESS
            self.save()

        # First validate
        if not self.validate_row():
            return False

        # Get our data
        data = self.cleaned

        self.convert_units(
            data, {
                fields.trees.PLOT_WIDTH:
                self.import_event.plot_width_conversion_factor,
                fields.trees.PLOT_LENGTH:
                self.import_event.plot_length_conversion_factor,
                fields.trees.DIAMETER:
                self.import_event.diameter_conversion_factor,
                fields.trees.TREE_HEIGHT:
                self.import_event.tree_height_conversion_factor,
                fields.trees.CANOPY_HEIGHT:
                self.import_event.canopy_height_conversion_factor
            })

        # We need the import event from treemap.models
        # the names of things are a bit odd here but
        # self.import_event ->
        #   TreeImportEvent (importer) ->
        #     ImportEvent (treemap)
        #
        base_treemap_import_event = self.import_event.base_import_event

        plot_edited = False
        tree_edited = False

        # Initially grab plot from row if it exists
        plot = self.plot
        if plot is None:
            plot = Plot(present=True)

        # Event if TREE_PRESENT is None, a tree
        # can still be spawned here if there is
        # any tree data later
        tree = plot.current_tree()

        # Check for an existing tree:
        if self.model_fields.OPENTREEMAP_ID_NUMBER in data:
            plot = Plot.objects.get(
                pk=data[self.model_fields.OPENTREEMAP_ID_NUMBER])
            tree = plot.current_tree()
        else:
            if data.get(self.model_fields.TREE_PRESENT, False):
                tree_edited = True
                if tree is None:
                    tree = Tree(present=True)

        data_owner = self.import_event.owner

        for modelkey, importdatakey in TreeImportRow.PLOT_MAP.iteritems():
            importdata = data.get(importdatakey, None)

            if importdata:
                plot_edited = True
                setattr(plot, modelkey, importdata)

        if plot_edited:
            plot.last_updated_by = data_owner
            plot.import_event = base_treemap_import_event
            plot.save()

        for modelkey, importdatakey in TreeImportRow.TREE_MAP.iteritems():
            importdata = data.get(importdatakey, None)

            if importdata:
                tree_edited = True
                if tree is None:
                    tree = Tree(present=True)
                setattr(tree, modelkey, importdata)

        if tree_edited:
            tree.last_updated_by = data_owner
            tree.import_event = base_treemap_import_event
            tree.plot = plot
            tree.save()

        self.plot = plot
        self.status = TreeImportRow.SUCCESS
        self.save()

        return True
Example #56
0
    def test_result_map(self):
        ##################################################################
        # Test main result map page
        # Note -> This page does not depend at all on the request
        #

        p1 = Plot(geometry=Point(50, 50),
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True,
                  width=100,
                  length=100,
                  data_owner=self.u)
        p2 = Plot(geometry=Point(60, 50),
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True,
                  width=90,
                  length=110,
                  data_owner=self.u)

        p1.save()
        p2.save()

        # For max/min plot size
        p3 = Plot(geometry=Point(50, 50),
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True,
                  width=80,
                  length=120,
                  data_owner=self.u)
        p4 = Plot(geometry=Point(60, 50),
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True,
                  width=70,
                  length=130,
                  data_owner=self.u)
        p5 = Plot(geometry=Point(60, 50),
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True,
                  width=60,
                  length=70,
                  data_owner=self.u)

        p3.save()
        p4.save()
        p5.save()

        t3 = Tree(plot=p3,
                  species=None,
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True)
        t3.save()

        t4 = Tree(plot=p4,
                  species=None,
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True)
        t4.save()

        t5 = Tree(plot=p5,
                  species=None,
                  last_updated_by=self.u,
                  import_event=self.ie,
                  present=True)
        t5.save()

        t1 = Tree(plot=p1,
                  species=None,
                  last_updated_by=self.u,
                  import_event=self.ie)
        t1.present = True

        current_year = datetime.now().year
        t1.date_planted = date(1999, 9, 9)

        t2 = Tree(plot=p2,
                  species=None,
                  last_updated_by=self.u,
                  import_event=self.ie)
        t1.present = True

        t1.save()
        t2.save()

        set_auto_now(t1, "last_updated", False)
        t1.last_updated = date(1999, 9, 9)
        t1.save()

        response = self.client.get("/map/")
        req = response.context

        set_auto_now(t1, "last_updated", True)

        # t1 and t2 should be in the latest trees
        exp = set([t4.pk, t5.pk])
        got = set([t.pk for t in req['latest_trees']])

        self.assertTrue(exp <= got)

        # Check to verify platting dates
        self.assertEquals(int(req['min_year']), 1999)
        self.assertEquals(int(req['current_year']), current_year)

        # Correct min/max plot sizes
        self.assertEqual(int(req['min_plot']), 60)
        self.assertEqual(int(req['max_plot']), 130)

        min_updated = mktime(t1.last_updated.timetuple())
        max_updated = mktime(t2.last_updated.timetuple())

        self.assertEqual(req['min_updated'], min_updated)
        self.assertEqual(req['max_updated'], max_updated)
Example #57
0
class TreeTest(OTMTestCase):
    def setUp(self):
        self.p = Point(-7615441.0, 5953519.0)

        self.instance = make_instance(point=self.p)
        self.user = make_commander_user(self.instance)

        self.plot = Plot(geom=self.instance.center, instance=self.instance)
        self.plot.save_with_user(self.user)
        self.tree = Tree(plot=self.plot, instance=self.instance)

    def test_negative_diameter_fails_validation(self):
        self.tree.diameter = '-1'
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception, 'diameter')

    def test_too_large_diameter_fails_validation(self):
        self.tree.diameter = str(Species.DEFAULT_MAX_DIAMETER + 1)
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception, 'diameter')

    def test_diameter_too_large_for_species_fails_validation(self):
        max_diameter = 1
        s = Species(genus='Ulmus',
                    species='rubra',
                    cultivar='Columella',
                    instance=self.instance,
                    max_diameter=max_diameter)
        s.save_with_user(self.user)
        self.tree.species = s
        self.tree.diameter = str(max_diameter + 1)
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception, 'diameter')

    def test_negative_height_fails_validation(self):
        self.tree.height = '-1'
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception, 'height')

    def test_too_large_height_fails_validation(self):
        self.tree.height = Species.DEFAULT_MAX_HEIGHT + 1
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception, 'height')

    def test_height_too_large_for_species_fails_validation(self):
        max_height = 1
        s = Species(genus='Ulmus',
                    species='rubra',
                    cultivar='Columella',
                    instance=self.instance,
                    max_height=max_height)
        s.save_with_user(self.user)
        self.tree.species = s
        self.tree.height = str(max_height + 1)
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception, 'height')

    def test_negative_canopy_height_fails_validation(self):
        self.tree.canopy_height = -1
        with self.assertRaises(ValidationError) as cm:
            self.tree.save_with_user(self.user)
        self.assertValidationErrorDictContainsKey(cm.exception,
                                                  'canopy_height')
Example #58
0
def context_dict_for_plot(request, plot, tree_id=None, **kwargs):
    context = context_dict_for_map_feature(request, plot, **kwargs)

    instance = request.instance
    user = request.user

    if tree_id:
        tree = get_object_or_404(Tree,
                                 instance=instance,
                                 plot=plot,
                                 pk=tree_id)
    else:
        tree = plot.current_tree()

    if tree:
        tree.convert_to_display_units()

    if tree is not None:
        photos = tree.photos()
        # can't send a regular photo qs because the API will
        # serialize this to JSON, which is not supported for qs
        context['photos'] = [context_dict_for_photo(request, photo)
                             for photo in photos]
    else:
        photos = []

    has_tree_diameter = tree is not None and tree.diameter is not None
    has_tree_species_with_code = tree is not None \
        and tree.species is not None and tree.species.otm_code is not None
    has_photo = tree is not None and len(photos) > 0

    total_progress_items = 4
    completed_progress_items = 1  # there is always a plot

    if has_tree_diameter:
        completed_progress_items += 1
    if has_tree_species_with_code:
        completed_progress_items += 1
    if has_photo:
        completed_progress_items += 1

    context['progress_percent'] = int(100 * (
        completed_progress_items / total_progress_items))

    context['progress_messages'] = []
    if not tree:
        context['progress_messages'].append(_('Add a tree'))
    if not has_tree_diameter:
        context['progress_messages'].append(_('Add the diameter'))
    if not has_tree_species_with_code:
        context['progress_messages'].append(_('Add the species'))
    if not has_photo:
        context['progress_messages'].append(_('Add a photo'))

    url_kwargs = {'instance_url_name': instance.url_name,
                  'feature_id': plot.pk}
    if tree:
        url_name = 'add_photo_to_tree'
        url_kwargs = dict(url_kwargs.items() + [('tree_id', tree.pk)])
    else:
        url_name = 'add_photo_to_plot'

    context['upload_photo_endpoint'] = reverse(url_name, kwargs=url_kwargs)

    context['plot'] = plot
    context['has_tree'] = tree is not None
    # Give an empty tree when there is none in order to show tree fields easily
    context['tree'] = tree or Tree(plot=plot, instance=instance)

    context['photo_upload_share_text'] = _photo_upload_share_text(
        plot, tree is not None)

    pmfs = PolygonalMapFeature.objects.filter(polygon__contains=plot.geom)

    if pmfs:
        context['containing_polygonalmapfeature'] = pmfs[0].cast_to_subtype()

    audits = _plot_audits(user, instance, plot)

    _add_audits_to_context(audits, context)

    _add_share_context(context, request, photos)

    return context
Example #59
0
    def setUp(self):
        ######
        # Request/Render mock
        ######
        def local_render_to_response(*args, **kwargs):
            from django.template import loader, RequestContext
            from django.http import HttpResponse

            httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype', None)}
            hr = HttpResponse(loader.render_to_string(*args, **kwargs),
                              **httpresponse_kwargs)

            if hasattr(args[1], 'dicts'):
                hr.request_context = args[1].dicts

            return hr

        django.shortcuts.render_to_response = local_render_to_response

        ######
        # Content types
        ######
        r1 = ReputationAction(name="edit verified", description="blah")
        r2 = ReputationAction(name="edit tree", description="blah")
        r3 = ReputationAction(name="Administrative Action", description="blah")
        r4 = ReputationAction(name="add tree", description="blah")
        r5 = ReputationAction(name="edit plot", description="blah")
        r6 = ReputationAction(name="add plot", description="blah")

        self.ra = [r1, r2, r3, r4, r5, r6]

        for r in self.ra:
            r.save()

        ######
        # Set up benefit values
        ######
        bv = BenefitValues(co2=0.02,
                           pm10=9.41,
                           area="InlandValleys",
                           electricity=0.1166,
                           voc=4.69,
                           ozone=5.0032,
                           natural_gas=1.25278,
                           nox=12.79,
                           stormwater=0.0078,
                           sox=3.72,
                           bvoc=4.96)

        bv.save()
        self.bv = bv

        dbh = "[1.0, 2.0, 3.0]"

        rsrc = Resource(meta_species="BDM_OTHER",
                        electricity_dbh=dbh,
                        co2_avoided_dbh=dbh,
                        aq_pm10_dep_dbh=dbh,
                        region="Sim City",
                        aq_voc_avoided_dbh=dbh,
                        aq_pm10_avoided_dbh=dbh,
                        aq_ozone_dep_dbh=dbh,
                        aq_nox_avoided_dbh=dbh,
                        co2_storage_dbh=dbh,
                        aq_sox_avoided_dbh=dbh,
                        aq_sox_dep_dbh=dbh,
                        bvoc_dbh=dbh,
                        co2_sequestered_dbh=dbh,
                        aq_nox_dep_dbh=dbh,
                        hydro_interception_dbh=dbh,
                        natural_gas_dbh=dbh)
        rsrc.save()
        self.rsrc = rsrc

        ######
        # Users
        ######
        u = User.objects.filter(username="******")

        if u:
            u = u[0]
        else:
            u = User.objects.create_user("jim", "*****@*****.**", "jim")
            u.is_staff = True
            u.is_superuser = True
            u.save()
            up = UserProfile(user=u)
            u.reputation = Reputation(user=u)
            u.reputation.save()

        self.u = u

        #######
        # Setup geometries -> Two stacked 100x100 squares
        #######
        n1geom = MultiPolygon(
            Polygon(((0, 0), (100, 0), (100, 100), (0, 100), (0, 0))))
        n2geom = MultiPolygon(
            Polygon(((0, 101), (101, 101), (101, 200), (0, 200), (0, 101))))

        n1 = Neighborhood(name="n1",
                          region_id=2,
                          city="c1",
                          state="PA",
                          county="PAC",
                          geometry=n1geom)
        n2 = Neighborhood(name="n2",
                          region_id=2,
                          city="c2",
                          state="NY",
                          county="NYC",
                          geometry=n2geom)

        n1.save()
        n2.save()

        z1geom = MultiPolygon(
            Polygon(((0, 0), (100, 0), (100, 100), (0, 100), (0, 0))))
        z2geom = MultiPolygon(
            Polygon(((0, 100), (100, 100), (100, 200), (0, 200), (0, 100))))

        z1 = ZipCode(zip="19107", geometry=z1geom)
        z2 = ZipCode(zip="10001", geometry=z2geom)

        z1.save()
        z2.save()

        exgeom1 = MultiPolygon(
            Polygon(((0, 0), (25, 0), (25, 25), (0, 25), (0, 0))))
        ex1 = ExclusionMask(geometry=exgeom1, type="building")

        ex1.save()

        agn1 = AggregateNeighborhood(annual_stormwater_management=0.0,
                                     annual_electricity_conserved=0.0,
                                     annual_energy_conserved=0.0,
                                     annual_natural_gas_conserved=0.0,
                                     annual_air_quality_improvement=0.0,
                                     annual_co2_sequestered=0.0,
                                     annual_co2_avoided=0.0,
                                     annual_co2_reduced=0.0,
                                     total_co2_stored=0.0,
                                     annual_ozone=0.0,
                                     annual_nox=0.0,
                                     annual_pm10=0.0,
                                     annual_sox=0.0,
                                     annual_voc=0.0,
                                     annual_bvoc=0.0,
                                     total_trees=0,
                                     total_plots=0,
                                     location=n1)

        agn2 = AggregateNeighborhood(annual_stormwater_management=0.0,
                                     annual_electricity_conserved=0.0,
                                     annual_energy_conserved=0.0,
                                     annual_natural_gas_conserved=0.0,
                                     annual_air_quality_improvement=0.0,
                                     annual_co2_sequestered=0.0,
                                     annual_co2_avoided=0.0,
                                     annual_co2_reduced=0.0,
                                     total_co2_stored=0.0,
                                     annual_ozone=0.0,
                                     annual_nox=0.0,
                                     annual_pm10=0.0,
                                     annual_sox=0.0,
                                     annual_voc=0.0,
                                     annual_bvoc=0.0,
                                     total_trees=0,
                                     total_plots=0,
                                     location=n2)

        agn1.save()
        agn2.save()

        self.agn1 = agn1
        self.agn2 = agn2

        self.z1 = z1
        self.z2 = z2
        self.n1 = n1
        self.n2 = n2

        ######
        # And we could use a few species...
        ######
        s1 = Species(symbol="s1", genus="testus1", species="specieius1")
        s2 = Species(symbol="s2", genus="testus2", species="specieius2")

        s1.save()
        s2.save()

        self.s1 = s1
        self.s2 = s2

        #######
        # Create some basic plots
        #######
        ie = ImportEvent(file_name='site_add')
        ie.save()

        self.ie = ie

        p1_no_tree = Plot(geometry=Point(50, 50),
                          last_updated_by=u,
                          import_event=ie,
                          present=True,
                          data_owner=u)
        p1_no_tree.save()

        p2_tree = Plot(geometry=Point(51, 51),
                       last_updated_by=u,
                       import_event=ie,
                       present=True,
                       data_owner=u)
        p2_tree.save()

        p3_tree_species1 = Plot(geometry=Point(50, 100),
                                last_updated_by=u,
                                import_event=ie,
                                present=True,
                                data_owner=u)
        p3_tree_species1.save()

        p4_tree_species2 = Plot(geometry=Point(50, 150),
                                last_updated_by=u,
                                import_event=ie,
                                present=True,
                                data_owner=u)
        p4_tree_species2.save()

        t1 = Tree(plot=p2_tree,
                  species=None,
                  last_updated_by=u,
                  import_event=ie)
        t1.present = True
        t1.save()

        t2 = Tree(plot=p3_tree_species1,
                  species=s1,
                  last_updated_by=u,
                  import_event=ie)
        t2.present = True
        t2.save()

        t3 = Tree(plot=p4_tree_species2,
                  species=s2,
                  last_updated_by=u,
                  import_event=ie)
        t3.present = True
        t3.save()

        self.p1_no_tree = p1_no_tree
        self.p2_tree = p2_tree
        self.p3_tree_species1 = p3_tree_species1
        self.p4_tree_species2 = p4_tree_species2

        self.plots = [p1_no_tree, p2_tree, p3_tree_species1, p4_tree_species2]

        self.t1 = t1
        self.t2 = t2
        self.t3 = t3
Example #60
0
 def test_add_tree_sets_updated(self):
     tree = Tree(diameter=10, plot=self.plot, instance=self.instance)
     tree.save_with_user(self.fellow)
     self.plot.refresh_from_db()
     self.assertGreater(self.plot.updated_at, self.initial_updated)
     self.assertEqual(self.plot.updated_by, self.fellow)