Skip to content

Commit

Permalink
Merge pull request #178 from ahinz/topic/merge-species
Browse files Browse the repository at this point in the history
Add a tool to merge duplicate species
  • Loading branch information
ahinz committed Dec 9, 2013
2 parents f6b7203 + 7713fa0 commit 88e4e51
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 3 deletions.
78 changes: 76 additions & 2 deletions importer/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,89 @@
from api.test_utils import setupTreemapEnv, mkPlot

from importer.views import create_rows_for_event, \
process_csv, process_status, commit
process_csv, process_status, commit, merge_species

from importer import errors,fields

from importer.models import TreeImportEvent, TreeImportRow, \
SpeciesImportEvent, SpeciesImportRow

from django_reputation.models import Reputation

from treemap.models import Species, Neighborhood, Plot, \
ExclusionMask, Resource, ImportEvent
ExclusionMask, Resource, ImportEvent, Tree

class MergeTest(TestCase):
def setUp(self):
setupTreemapEnv()

self.user = User(username='smith')
self.user.save()
self.user.reputation = Reputation()
self.user.save()

ie = ImportEvent(file_name="bie1")
ie.save()

p1 = mkPlot(self.user, geom=Point(25.0000001,25.0000001))
p1.import_event = ie
p1.save()

p2 = mkPlot(self.user, geom=Point(25.0000002,25.0000002))
p2.import_event = ie
p2.save()

ss = Species.objects.all()
self.s1 = ss[0]
self.s2 = ss[1]

self.t1 = Tree(plot=p1, species=self.s1, last_updated_by=self.user)
self.t1.import_event = ie
self.t1.save()

self.t2 = Tree(plot=p2, species=self.s2, last_updated_by=self.user)
self.t2.import_event = ie
self.t2.save()

def test_cant_merge_same_species(self):
r = HttpRequest()
r.REQUEST = {
'species_to_delete': self.s1.pk,
'species_to_replace_with': self.s1.pk
}

r.user = self.user
r.user.is_staff = True

spcnt = Species.objects.all().count()

resp = merge_species(r)

self.assertEqual(Species.objects.all().count(), spcnt)
self.assertEqual(resp.status_code, 400)


def test_merges(self):
r = HttpRequest()
r.REQUEST = {
'species_to_delete': self.s1.pk,
'species_to_replace_with': self.s2.pk
}

r.user = self.user
r.user.is_staff = True

merge_species(r)

self.assertRaises(Species.DoesNotExist,
Species.objects.get, pk=self.s1.pk)

t1r = Tree.objects.get(pk=self.t1.pk)
t2r = Tree.objects.get(pk=self.t2.pk)

self.assertEqual(t1r.species.pk, self.s2.pk)
self.assertEqual(t2r.species.pk, self.s2.pk)


class ValidationTest(TestCase):
def setUp(self):
Expand Down
1 change: 1 addition & 0 deletions importer/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
url(r'^export/tree/(?P<import_event_id>\d+)$', 'export_single_tree_import', name='export_single_tree_import'),

# API
url(r'^api/merge$', 'merge_species', name='merge'),
url(r'^api/(?P<import_type>[a-z]+)/(?P<import_event_id>\d+)/results/(?P<subtype>[a-zA-Z]+)$', 'results', name='results'),
url(r'^api/(?P<import_type>[a-z]+)/(?P<import_event_id>\d+)/commit$', 'commit', name='commit'),
url(r'^api/(?P<import_type>[a-z]+)/(?P<import_event_id>\d+)/update$', 'update', name='update'),
Expand Down
30 changes: 29 additions & 1 deletion importer/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,35 @@ def list_imports(request):
{'trees_active': active_trees,
'trees_finished': finished_trees,
'species_active': active_species,
'species_finished': finished_species }))
'species_finished': finished_species,
'all_species': Species.objects.all() }))

@login_required
@transaction.commit_on_success
def merge_species(request):
if not request.user.is_staff:
raise Exception("Must be admin")

species_to_delete_id = request.REQUEST['species_to_delete']
species_to_replace_with_id = request.REQUEST['species_to_replace_with']

species_to_delete = Species.objects.get(pk=species_to_delete_id)
species_to_replace_with = Species.objects.get(pk=species_to_replace_with_id)

if species_to_delete.pk == species_to_replace_with.pk:
return HttpResponse(
json.dumps({"error": "Must pick different species"}),
content_type = 'application/json',
status=400)

Tree.objects.filter(species=species_to_delete)\
.update(species=species_to_replace_with)

species_to_delete.delete()

return HttpResponse(
json.dumps({"status": "ok"}),
content_type = 'application/json')

@login_required
def show_species_import_status(request, import_event_id):
Expand Down
35 changes: 35 additions & 0 deletions static/js/importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,41 @@ I.init.list = function() {
var numPrevSpeciesCounts = -1;
var numPrevTreeCounts = -1;

var $merge_errors = $("#merge-errors");
$("#merge-species").click(function() {
var species_to_delete = $("#species-to-remove").val(),
species_to_replace_with = $("#species-to-update-to").val();

$merge_errors.empty();

var req = $.ajax({
url: I.api_prefix + 'merge',
type: "POST",
data: {
species_to_delete: species_to_delete,
species_to_replace_with: species_to_replace_with
}
});

req.fail(function(e) {
var message = "An error occured, please try again later";
if (e.responseText && e.responseText[0] == '{') {
var parsed = JSON.parse(e.responseText);
if (parsed.error) {
message = parsed.error;
}
}
$merge_errors.html(message);
});

req.done(function(f) {
$merge_errors.html("Success!");

$("#species-to-remove option[value=" + species_to_delete + "]").remove();
$("#species-to-update-to option[value=" + species_to_delete + "]").remove();
});
});

function update_counts() {
if ($("tr[data-running=true]").length > 0) {
I.api.getUpdatedCounts()
Expand Down
22 changes: 22 additions & 0 deletions templates/importer/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,28 @@ <h3>
Export All Species
</a>
</h3>
<div>
<h2>Merge Species</h2>
<div id="merge-errors"></div>
<div>
Remove species:
<select id="species-to-remove">
{% for s in all_species %}
<option value="{{ s.pk }}">{{ s.scientific_name }} ({{ s.pk }})</option>
{% endfor %}
</select>
</div>
<div>
Replace with:
<select id="species-to-update-to">
{% for s in all_species %}
<option value="{{ s.pk }}">{{ s.scientific_name }} ({{ s.pk }})</option>
{% endfor %}
</select>
</div>

<button id="merge-species">Merge</button>
</div>
</div>

{% endblock %}
Expand Down

0 comments on commit 88e4e51

Please sign in to comment.