Esempio n. 1
0
File: tests.py Progetto: gapb/OTM2
    def setUp(self):
        center_point = Point(25, 25, srid=4326)
        center_point.transform(3857)
        self.instance = make_instance(point=center_point, edge_length=500000)
        self.user = make_admin_user(self.instance)

        self.ie = TreeImportEvent(file_name='file',
                                  owner=self.user,
                                  instance=self.instance)
        self.ie.save()
Esempio n. 2
0
def download_import_template(request, instance, import_type):
    if import_type == SpeciesImportEvent.import_type:
        filename = 'OpenTreeMap_Species_Import_Template.csv'
        field_names = fields.title_case(fields.species.ALL)
    else:
        filename = 'OpenTreeMap_Tree_Import_Template.csv'
        ie = TreeImportEvent(instance=instance)
        field_names = ie.ordered_legal_fields_title_case()

    response = HttpResponse(content_type="text/csv")
    response["Content-Disposition"] = "attachment; filename=%s" % filename
    writer = csv.DictWriter(response, field_names)
    writer.writeheader()

    return response
Esempio n. 3
0
def download_import_template(request, instance, import_type):
    if import_type == SpeciesImportEvent.import_type:
        filename = 'OpenTreeMap_Species_Import_Template.csv'
        field_names = fields.title_case(fields.species.ALL)
    else:
        filename = 'OpenTreeMap_Tree_Import_Template.csv'
        ie = TreeImportEvent(instance=instance)
        field_names = ie.ordered_legal_fields_title_case()

    response = HttpResponse(content_type="text/csv")
    response["Content-Disposition"] = "attachment; filename=%s" % filename
    writer = csv.DictWriter(response, field_names)
    writer.writeheader()

    return response
Esempio n. 4
0
    def test_missing_point_field(self):
        ie = TreeImportEvent(file_name="file", owner=self.user, instance=self.instance)
        ie.save()

        TreeImportRow.objects.count()

        c = self.write_csv([["Plot width", "Plot length"], ["5", "5"], ["8", "8"]])

        rslt = _create_rows_for_event(ie, c)

        self.assertFalse(rslt)

        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.MISSING_FIELD)
Esempio n. 5
0
    def setUp(self):
        center_point = Point(25, 25, srid=4326)
        center_point.transform(3857)
        self.instance = make_instance(point=center_point, edge_length=500000)
        self.user = make_admin_user(self.instance)

        self.ie = TreeImportEvent(file_name="file", owner=self.user, instance=self.instance)
        self.ie.save()
Esempio n. 6
0
File: tests.py Progetto: gapb/OTM2
    def test_missing_point_field(self):
        ie = TreeImportEvent(file_name='file',
                             owner=self.user,
                             instance=self.instance)
        ie.save()

        TreeImportRow.objects.count()

        c = self.write_csv([['Plot width', 'Plot length'], ['5', '5'],
                            ['8', '8']])

        rslt = _create_rows_for_event(ie, c)

        self.assertFalse(rslt)

        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.MISSING_FIELD)
Esempio n. 7
0
    def test_empty_file_error(self):
        ie = TreeImportEvent(file_name="file", owner=self.user, instance=self.instance)
        ie.save()

        base_rows = TreeImportRow.objects.count()

        c = self.write_csv([["point x", "point y"]])

        rslt = _create_rows_for_event(ie, c)

        # No rows added and validation failed
        self.assertEqual(TreeImportRow.objects.count(), base_rows)
        self.assertFalse(rslt)

        # The only error is a bad file error
        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.EMPTY_FILE)
Esempio n. 8
0
def download_import_template(request, instance, import_type):
    if import_type == SpeciesImportEvent.import_type:
        filename = 'OpenTreeMap_Species_Import_Template.csv'
        field_names = fields.title_case(fields.species.ALL)
    else:
        filename = 'OpenTreeMap_Tree_Import_Template.csv'
        ie = TreeImportEvent(instance=instance)
        field_names = ie.ordered_legal_fields_title_case()

    # Encoding the field names prevents an error when the field names have
    # non-ASCII characters.
    field_names = [field_name.encode('utf-8') for field_name in field_names]

    response = HttpResponse(content_type="text/csv")
    response["Content-Disposition"] = "attachment; filename=%s" % filename
    writer = csv.DictWriter(response, field_names)
    writer.writeheader()

    return response
Esempio n. 9
0
    def test_empty_file_error(self):
        ie = TreeImportEvent(file_name='file', owner=self.user,
                             instance=self.instance)
        ie.save()

        base_rows = TreeImportRow.objects.count()

        c = self.write_csv([['point x', 'point y']])

        rslt = _create_rows_for_event(ie, c)

        # No rows added and validation failed
        self.assertEqual(TreeImportRow.objects.count(), base_rows)
        self.assertFalse(rslt)

        # The only error is a bad file error
        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.EMPTY_FILE)
Esempio n. 10
0
    def test_unknown_field(self):
        ie = TreeImportEvent(file_name='file', owner=self.user,
                             instance=self.instance)
        ie.save()

        TreeImportRow.objects.count()

        c = self.write_csv([
            ['street address', 'name', 'age', 'point x', 'point y'],
            ['123 Beach St', 'a', 'b', '5', '5'],
            ['222 Main St', 'a', 'b', '8', '8']])

        rslt = _create_rows_for_event(ie, c)

        self.assertFalse(rslt)

        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.UNMATCHED_FIELDS)
        self.assertEqual(set(ierrors[0]['data']), set(['name', 'age']))
Esempio n. 11
0
    def test_unknown_field(self):
        ie = TreeImportEvent(file_name='file', owner=self.user,
                             instance=self.instance)
        ie.save()

        TreeImportRow.objects.count()

        c = self.write_csv([
            ['street address', 'name', 'age', 'point x', 'point y'],
            ['123 Beach St', 'a', 'b', '5', '5'],
            ['222 Main St', 'a', 'b', '8', '8']])

        rslt = _create_rows_for_event(ie, c)

        self.assertFalse(rslt)

        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.UNMATCHED_FIELDS)
        self.assertEqual(set(ierrors[0]['data']), set(['name', 'age']))
Esempio n. 12
0
    def test_unknown_field(self):
        ie = TreeImportEvent(file_name="file", owner=self.user, instance=self.instance)
        ie.save()

        TreeImportRow.objects.count()

        c = self.write_csv(
            [
                ["street address", "name", "age", "point x", "point y"],
                ["123 Beach St", "a", "b", "5", "5"],
                ["222 Main St", "a", "b", "8", "8"],
            ]
        )

        rslt = _create_rows_for_event(ie, c)

        self.assertFalse(rslt)

        ierrors = json.loads(ie.errors)
        self.assertTrue(len(ierrors), 1)
        self.assertHasError(ie, errors.UNMATCHED_FIELDS)
        self.assertEqual(set(ierrors[0]["data"]), set(["name", "age"]))
Esempio n. 13
0
class TreeValidationTest(ValidationTest):
    def setUp(self):
        center_point = Point(25, 25, srid=4326)
        center_point.transform(3857)
        self.instance = make_instance(point=center_point, edge_length=500000)
        self.user = make_admin_user(self.instance)

        self.ie = TreeImportEvent(file_name='file',
                                  owner=self.user,
                                  instance=self.instance)
        self.ie.save()

    def mkrow(self, data):
        return TreeImportRow.objects.create(
            data=json.dumps(data), import_event=self.ie, idx=1)

    def test_udf(self):
        psycopg2.extras.register_hstore(connection.cursor(), globally=True)

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

        row = {'point x': '16',
               'point y': '20',
               'plot: test choice': 'a'}

        i = self.mkrow(row)
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_UDF_VALUE)

        row['plot: test choice'] = 'z'

        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.INVALID_UDF_VALUE)

    def test_species_diameter_and_height(self):
        s1_gsc = Species(instance=self.instance, genus='g1', species='s1',
                         cultivar='c1', max_height=30, max_diameter=19)
        s1_gs = Species(instance=self.instance, genus='g1', species='s1',
                        cultivar='', max_height=22, max_diameter=12)
        s1_gsc.save_with_system_user_bypass_auth()
        s1_gs.save_with_system_user_bypass_auth()

        row = {'point x': '16',
               'point y': '20',
               'genus': 'g1',
               'species': 's1',
               'diameter': '15',
               'tree height': '18'}

        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertNotHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

        row['tree height'] = 25
        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

        row['cultivar'] = 'c1'
        i = self.mkrow(row)
        i.validate_row()

        self.assertNotHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertNotHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

    def test_proximity(self):
        p1 = mkPlot(self.instance, self.user,
                    geom=Point(25.0000001, 25.0000001, srid=4326))

        p2 = mkPlot(self.instance, self.user,
                    geom=Point(25.0000002, 25.0000002, srid=4326))

        p3 = mkPlot(self.instance, self.user,
                    geom=Point(25.0000003, 25.0000003, srid=4326))

        p4 = mkPlot(self.instance, self.user,
                    geom=Point(27.0000001, 27.0000001, srid=4326))

        n1 = {p.pk for p in [p1, p2, p3]}
        n2 = {p4.pk}

        i = self.mkrow({'point x': '25.0000001',
                        'point y': '25.0000001'})
        i.validate_row()

        self.assertHasError(i, errors.DUPLICATE_TREE)

        i = self.mkrow({'point x': '25.00000025',
                        'point y': '25.00000025'})
        i.validate_row()

        self.assertHasError(i, errors.NEARBY_TREES, n1, set)

        i = self.mkrow({'point x': '27.00000015',
                        'point y': '27.00000015'})
        i.validate_row()

        self.assertHasError(i, errors.NEARBY_TREES, n2, set)

        i = self.mkrow({'point x': '30.00000015',
                        'point y': '30.00000015'})
        i.validate_row()

        self.assertNotHasError(i, errors.NEARBY_TREES)

    def test_species_id(self):
        s1_gsc = Species(instance=self.instance, genus='g1', species='s1',
                         cultivar='c1')
        s1_gs = Species(instance=self.instance, genus='g1', species='s1',
                        cultivar='')
        s1_g = Species(instance=self.instance, genus='g1', species='',
                       cultivar='')

        s2_gsc = Species(instance=self.instance, genus='g2', species='s2',
                         cultivar='c2')
        s2_gs = Species(instance=self.instance, genus='g2', species='s2',
                        cultivar='')

        for s in [s1_gsc, s1_gs, s1_g, s2_gsc, s2_gs]:
            s.save_with_system_user_bypass_auth()

        # Simple genus, species, cultivar matches
        i = self.mkrow({'point x': '16',
                        'point y': '20',
                        'genus': 'g1'})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({'point x': '16',
                        'point y': '20',
                        'genus': 'g1',
                        'species': 's1'})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({'point x': '16',
                        'point y': '20',
                        'genus': 'g1',
                        'species': 's1',
                        'cultivar': 'c1'})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        # Test no species info at all
        i = self.mkrow({'point x': '16',
                        'point y': '20'})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        # Test mismatches
        i = self.mkrow({'point x': '16',
                        'point y': '20',
                        'genus': 'g1',
                        'species': 's2',
                        'cultivar': 'c1'})
        i.validate_row()

        self.assertHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({'point x': '16',
                        'point y': '20',
                        'genus': 'g2'})
        i.validate_row()

        self.assertHasError(i, errors.INVALID_SPECIES)

    def test_otm_id(self):
        # silly invalid-int-errors should be caught
        i = self.mkrow({'point x': '16',
                        'point y': '20',
                        'opentreemap plot id': '44b'})
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INT_ERROR, None)

        i = self.mkrow({'point x': '25',
                        'point y': '25',
                        'opentreemap plot id': '-22'})
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.POS_INT_ERROR)

        # With no plots in the system, all ids should fail
        i = self.mkrow({'point x': '25',
                        'point y': '25',
                        'opentreemap plot id': '44'})
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_OTM_ID)

        p = mkPlot(self.instance, self.user)

        # With an existing plot it should be fine
        i = self.mkrow({'point x': '25',
                        'point y': '25',
                        'opentreemap plot id': p.pk})
        r = i.validate_row()

        self.assertNotHasError(i, errors.INVALID_OTM_ID)
        self.assertNotHasError(i, errors.INT_ERROR)

    def test_geom_validation(self):
        def mkpt(x, y):
            return self.mkrow({'point x': str(x), 'point y': str(y)})

        # Invalid numbers
        i = mkpt('300a', '20b')
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.FLOAT_ERROR)

        # Crazy lat/lngs
        i = mkpt(300, 20)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_GEOM)

        i = mkpt(50, 93)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_GEOM)

        i = mkpt(55, 55)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.GEOM_OUT_OF_BOUNDS)

        i = mkpt(-5, -5)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.GEOM_OUT_OF_BOUNDS)

        # This should work...
        i = mkpt(25, 25)
        r = i.validate_row()

        # Can't assert that r is true because other validation
        # logic may have tripped it
        self.assertNotHasError(i, errors.GEOM_OUT_OF_BOUNDS)
        self.assertNotHasError(i, errors.INVALID_GEOM)
        self.assertNotHasError(i, errors.FLOAT_ERROR)
Esempio n. 14
0
File: tests.py Progetto: gapb/OTM2
class TreeValidationTest(ValidationTest):
    def setUp(self):
        center_point = Point(25, 25, srid=4326)
        center_point.transform(3857)
        self.instance = make_instance(point=center_point, edge_length=500000)
        self.user = make_admin_user(self.instance)

        self.ie = TreeImportEvent(file_name='file',
                                  owner=self.user,
                                  instance=self.instance)
        self.ie.save()

    def mkrow(self, data):
        return TreeImportRow.objects.create(data=json.dumps(data),
                                            import_event=self.ie,
                                            idx=1)

    def test_udf(self):
        psycopg2.extras.register_hstore(connection.cursor(), globally=True)

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

        row = {'point x': '16', 'point y': '20', 'plot: test choice': 'a'}

        i = self.mkrow(row)
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_UDF_VALUE)

        row['plot: test choice'] = 'z'

        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.INVALID_UDF_VALUE)

    def test_species_diameter_and_height(self):
        s1_gsc = Species(instance=self.instance,
                         genus='g1',
                         species='s1',
                         cultivar='c1',
                         max_height=30,
                         max_diameter=19)
        s1_gs = Species(instance=self.instance,
                        genus='g1',
                        species='s1',
                        cultivar='',
                        max_height=22,
                        max_diameter=12)
        s1_gsc.save_with_system_user_bypass_auth()
        s1_gs.save_with_system_user_bypass_auth()

        row = {
            'point x': '16',
            'point y': '20',
            'genus': 'g1',
            'species': 's1',
            'diameter': '15',
            'tree height': '18'
        }

        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertNotHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

        row['tree height'] = 25
        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

        row['cultivar'] = 'c1'
        i = self.mkrow(row)
        i.validate_row()

        self.assertNotHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertNotHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

    def test_proximity(self):
        p1 = mkPlot(self.instance,
                    self.user,
                    geom=Point(25.0000001, 25.0000001, srid=4326))

        p2 = mkPlot(self.instance,
                    self.user,
                    geom=Point(25.0000002, 25.0000002, srid=4326))

        p3 = mkPlot(self.instance,
                    self.user,
                    geom=Point(25.0000003, 25.0000003, srid=4326))

        p4 = mkPlot(self.instance,
                    self.user,
                    geom=Point(27.0000001, 27.0000001, srid=4326))

        n1 = {p.pk for p in [p1, p2, p3]}
        n2 = {p4.pk}

        i = self.mkrow({'point x': '25.0000001', 'point y': '25.0000001'})
        i.validate_row()

        self.assertHasError(i, errors.DUPLICATE_TREE)

        i = self.mkrow({'point x': '25.00000025', 'point y': '25.00000025'})
        i.validate_row()

        self.assertHasError(i, errors.NEARBY_TREES, n1, set)

        i = self.mkrow({'point x': '27.00000015', 'point y': '27.00000015'})
        i.validate_row()

        self.assertHasError(i, errors.NEARBY_TREES, n2, set)

        i = self.mkrow({'point x': '30.00000015', 'point y': '30.00000015'})
        i.validate_row()

        self.assertNotHasError(i, errors.NEARBY_TREES)

    def test_species_id(self):
        s1_gsc = Species(instance=self.instance,
                         genus='g1',
                         species='s1',
                         cultivar='c1')
        s1_gs = Species(instance=self.instance,
                        genus='g1',
                        species='s1',
                        cultivar='')
        s1_g = Species(instance=self.instance,
                       genus='g1',
                       species='',
                       cultivar='')

        s2_gsc = Species(instance=self.instance,
                         genus='g2',
                         species='s2',
                         cultivar='c2')
        s2_gs = Species(instance=self.instance,
                        genus='g2',
                        species='s2',
                        cultivar='')

        for s in [s1_gsc, s1_gs, s1_g, s2_gsc, s2_gs]:
            s.save_with_system_user_bypass_auth()

        # Simple genus, species, cultivar matches
        i = self.mkrow({'point x': '16', 'point y': '20', 'genus': 'g1'})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({
            'point x': '16',
            'point y': '20',
            'genus': 'g1',
            'species': 's1'
        })
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({
            'point x': '16',
            'point y': '20',
            'genus': 'g1',
            'species': 's1',
            'cultivar': 'c1'
        })
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        # Test no species info at all
        i = self.mkrow({'point x': '16', 'point y': '20'})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        # Test mismatches
        i = self.mkrow({
            'point x': '16',
            'point y': '20',
            'genus': 'g1',
            'species': 's2',
            'cultivar': 'c1'
        })
        i.validate_row()

        self.assertHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({'point x': '16', 'point y': '20', 'genus': 'g2'})
        i.validate_row()

        self.assertHasError(i, errors.INVALID_SPECIES)

    def test_otm_id(self):
        # silly invalid-int-errors should be caught
        i = self.mkrow({
            'point x': '16',
            'point y': '20',
            'opentreemap plot id': '44b'
        })
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INT_ERROR, None)

        i = self.mkrow({
            'point x': '25',
            'point y': '25',
            'opentreemap plot id': '-22'
        })
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.POS_INT_ERROR)

        # With no plots in the system, all ids should fail
        i = self.mkrow({
            'point x': '25',
            'point y': '25',
            'opentreemap plot id': '44'
        })
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_OTM_ID)

        p = mkPlot(self.instance, self.user)

        # With an existing plot it should be fine
        i = self.mkrow({
            'point x': '25',
            'point y': '25',
            'opentreemap plot id': p.pk
        })
        r = i.validate_row()

        self.assertNotHasError(i, errors.INVALID_OTM_ID)
        self.assertNotHasError(i, errors.INT_ERROR)

    def test_geom_validation(self):
        def mkpt(x, y):
            return self.mkrow({'point x': str(x), 'point y': str(y)})

        # Invalid numbers
        i = mkpt('300a', '20b')
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.FLOAT_ERROR)

        # Crazy lat/lngs
        i = mkpt(300, 20)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_GEOM)

        i = mkpt(50, 93)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_GEOM)

        i = mkpt(55, 55)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.GEOM_OUT_OF_BOUNDS)

        i = mkpt(-5, -5)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.GEOM_OUT_OF_BOUNDS)

        # This should work...
        i = mkpt(25, 25)
        r = i.validate_row()

        # Can't assert that r is true because other validation
        # logic may have tripped it
        self.assertNotHasError(i, errors.GEOM_OUT_OF_BOUNDS)
        self.assertNotHasError(i, errors.INVALID_GEOM)
        self.assertNotHasError(i, errors.FLOAT_ERROR)
Esempio n. 15
0
class TreeValidationTest(ValidationTest):
    def setUp(self):
        center_point = Point(25, 25, srid=4326)
        center_point.transform(3857)
        self.instance = make_instance(point=center_point, edge_length=500000)
        self.user = make_admin_user(self.instance)

        self.ie = TreeImportEvent(file_name="file", owner=self.user, instance=self.instance)
        self.ie.save()

    def mkrow(self, data):
        return TreeImportRow.objects.create(data=json.dumps(data), import_event=self.ie, idx=1)

    def test_udf(self):
        psycopg2.extras.register_hstore(connection.cursor(), globally=True)

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

        row = {"point x": "16", "point y": "20", "plot: test choice": "a"}

        i = self.mkrow(row)
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_UDF_VALUE)

        row["plot: test choice"] = "z"

        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.INVALID_UDF_VALUE)

    def test_species_diameter_and_height(self):
        s1_gsc = Species(
            instance=self.instance, genus="g1", species="s1", cultivar="c1", max_height=30, max_diameter=19
        )
        s1_gs = Species(instance=self.instance, genus="g1", species="s1", cultivar="", max_height=22, max_diameter=12)
        s1_gsc.save_with_system_user_bypass_auth()
        s1_gs.save_with_system_user_bypass_auth()

        row = {"point x": "16", "point y": "20", "genus": "g1", "species": "s1", "diameter": "15", "tree height": "18"}

        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertNotHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

        row["tree height"] = 25
        i = self.mkrow(row)
        i.validate_row()

        self.assertHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

        row["cultivar"] = "c1"
        i = self.mkrow(row)
        i.validate_row()

        self.assertNotHasError(i, errors.SPECIES_DBH_TOO_HIGH)
        self.assertNotHasError(i, errors.SPECIES_HEIGHT_TOO_HIGH)

    def test_proximity(self):
        p1 = mkPlot(self.instance, self.user, geom=Point(25.0000001, 25.0000001, srid=4326))

        p2 = mkPlot(self.instance, self.user, geom=Point(25.0000002, 25.0000002, srid=4326))

        p3 = mkPlot(self.instance, self.user, geom=Point(25.0000003, 25.0000003, srid=4326))

        p4 = mkPlot(self.instance, self.user, geom=Point(27.0000001, 27.0000001, srid=4326))

        n1 = {p.pk for p in [p1, p2, p3]}
        n2 = {p4.pk}

        i = self.mkrow({"point x": "25.0000001", "point y": "25.0000001"})
        i.validate_row()

        self.assertHasError(i, errors.DUPLICATE_TREE)

        i = self.mkrow({"point x": "25.00000025", "point y": "25.00000025"})
        i.validate_row()

        self.assertHasError(i, errors.NEARBY_TREES, n1, set)

        i = self.mkrow({"point x": "27.00000015", "point y": "27.00000015"})
        i.validate_row()

        self.assertHasError(i, errors.NEARBY_TREES, n2, set)

        i = self.mkrow({"point x": "30.00000015", "point y": "30.00000015"})
        i.validate_row()

        self.assertNotHasError(i, errors.NEARBY_TREES)

    def test_species_id(self):
        s1_gsc = Species(instance=self.instance, genus="g1", species="s1", cultivar="c1")
        s1_gs = Species(instance=self.instance, genus="g1", species="s1", cultivar="")
        s1_g = Species(instance=self.instance, genus="g1", species="", cultivar="")

        s2_gsc = Species(instance=self.instance, genus="g2", species="s2", cultivar="c2")
        s2_gs = Species(instance=self.instance, genus="g2", species="s2", cultivar="")

        for s in [s1_gsc, s1_gs, s1_g, s2_gsc, s2_gs]:
            s.save_with_system_user_bypass_auth()

        # Simple genus, species, cultivar matches
        i = self.mkrow({"point x": "16", "point y": "20", "genus": "g1"})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({"point x": "16", "point y": "20", "genus": "g1", "species": "s1"})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({"point x": "16", "point y": "20", "genus": "g1", "species": "s1", "cultivar": "c1"})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        # Test no species info at all
        i = self.mkrow({"point x": "16", "point y": "20"})
        i.validate_row()

        self.assertNotHasError(i, errors.INVALID_SPECIES)

        # Test mismatches
        i = self.mkrow({"point x": "16", "point y": "20", "genus": "g1", "species": "s2", "cultivar": "c1"})
        i.validate_row()

        self.assertHasError(i, errors.INVALID_SPECIES)

        i = self.mkrow({"point x": "16", "point y": "20", "genus": "g2"})
        i.validate_row()

        self.assertHasError(i, errors.INVALID_SPECIES)

    def test_otm_id(self):
        # silly invalid-int-errors should be caught
        i = self.mkrow({"point x": "16", "point y": "20", "opentreemap plot id": "44b"})
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INT_ERROR, None)

        i = self.mkrow({"point x": "25", "point y": "25", "opentreemap plot id": "-22"})
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.POS_INT_ERROR)

        # With no plots in the system, all ids should fail
        i = self.mkrow({"point x": "25", "point y": "25", "opentreemap plot id": "44"})
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_OTM_ID)

        p = mkPlot(self.instance, self.user)

        # With an existing plot it should be fine
        i = self.mkrow({"point x": "25", "point y": "25", "opentreemap plot id": p.pk})
        r = i.validate_row()

        self.assertNotHasError(i, errors.INVALID_OTM_ID)
        self.assertNotHasError(i, errors.INT_ERROR)

    def test_geom_validation(self):
        def mkpt(x, y):
            return self.mkrow({"point x": str(x), "point y": str(y)})

        # Invalid numbers
        i = mkpt("300a", "20b")
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.FLOAT_ERROR)

        # Crazy lat/lngs
        i = mkpt(300, 20)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_GEOM)

        i = mkpt(50, 93)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.INVALID_GEOM)

        i = mkpt(55, 55)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.GEOM_OUT_OF_BOUNDS)

        i = mkpt(-5, -5)
        r = i.validate_row()

        self.assertFalse(r)
        self.assertHasError(i, errors.GEOM_OUT_OF_BOUNDS)

        # This should work...
        i = mkpt(25, 25)
        r = i.validate_row()

        # Can't assert that r is true because other validation
        # logic may have tripped it
        self.assertNotHasError(i, errors.GEOM_OUT_OF_BOUNDS)
        self.assertNotHasError(i, errors.INVALID_GEOM)
        self.assertNotHasError(i, errors.FLOAT_ERROR)