Skip to content

Commit

Permalink
Add ability to add new taxon with higher taxonomic ranks (#4103)
Browse files Browse the repository at this point in the history
  • Loading branch information
dimasciput authored Jul 25, 2024
1 parent 0f28002 commit 722baac
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 38 deletions.
26 changes: 20 additions & 6 deletions bims/api_views/taxon.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,12 @@ def post(self, request, *args):
author_name = self.request.POST.get('authorName', '')
rank = self.request.POST.get('rank', None)
family_id = self.request.POST.get('familyId', None)
family = None
parent = None
if family_id:
family = Taxonomy.objects.get(id=int(family_id))
parent = Taxonomy.objects.get(id=int(family_id))
parent_id = self.request.POST.get('parentId', None)
if parent_id:
parent = Taxonomy.objects.get(id=int(parent_id))
if gbif_key:
taxonomy = update_taxonomy_from_gbif(
key=gbif_key,
Expand All @@ -277,12 +280,23 @@ def post(self, request, *args):
)
if taxon_group_id:
taxon_group = TaxonGroup.objects.get(id=taxon_group_id)
taxon_group.taxonomies.add(taxonomy)
taxon_group.taxonomies.add(
taxonomy,
through_defaults={
'is_validated': False
}
)
else:
if taxon_group:
try:
taxon_group = TaxonGroup.objects.get(name=taxon_group)
taxon_group.taxonomies.add(taxonomy)
taxon_group.taxonomies.add(
taxonomy,
through_defaults={
'is_validated': False
}
)
taxon_group_id = taxon_group.id
except TaxonGroup.DoesNotExist:
pass
if taxonomy:
Expand All @@ -302,8 +316,8 @@ def post(self, request, *args):
taxonomy.ready_to_be_validate()
taxonomy.send_new_taxon_email(taxon_group_id)

if family:
taxonomy.parent = family
if parent:
taxonomy.parent = parent
taxonomy.save()

with transaction.atomic():
Expand Down
26 changes: 13 additions & 13 deletions bims/enums/taxonomic_rank.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@

class TaxonomicRank(Enum):

SUBSPECIES = 'Sub Species'
SPECIES = 'Species'
GENUS = 'Genus'
SUBGENUS = 'Sub Genus'
FAMILY = 'Family'
SUPERFAMILY = 'Super Family'
ORDER = 'Order'
CLASS = 'Class'
SUBCLASS = 'Sub Class'
DOMAIN = 'Domain'
KINGDOM = 'Kingdom'
PHYLUM = 'Phylum'
SUBPHYLUM = 'SubPhylum'
KINGDOM = 'Kingdom'
DOMAIN = 'Domain'
CLASS = 'Class'
SUBCLASS = 'Sub Class'
ORDER = 'Order'
SUBORDER = 'Sub Order'
INFRAORDER = 'Infra Order'
SUPERFAMILY = 'Super Family'
FAMILY = 'Family'
SUBFAMILY = 'Sub Family'
VARIETY = 'Variety'
FORMA = 'Forma'
TRIBE = 'Tribe'
SUBTRIBE = 'Sub Tribe'
GENUS = 'Genus'
SUBGENUS = 'Sub Genus'
SPECIES = 'Species'
SUBSPECIES = 'Sub Species'
VARIETY = 'Variety'
FORMA = 'Forma'

@staticmethod
def hierarchy():
Expand Down
36 changes: 24 additions & 12 deletions bims/static/js/taxa_management/add_new_taxon.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
export const addNewTaxon = (() => {
const $newTaxonFamilyIdInput = $('.new-taxon-family-id');
const $newTaxonParentIdInput = $('.new-taxon-parent-id');
const $newTaxonFamilyInput = $('.new-taxon-family-name');
const $newTaxonParent = $('#new-taxon-parent');
const $taxonForm = $('.new-taxon-form');
const $addNewTaxonBtn = $taxonForm.find('.add-new-taxon-btn');
const $newTaxonNameInput = $('#new-taxon-name');

let selectedTaxonGroup = '';


function showNewTaxonForm(taxonName) {
let capitalizedTaxonName = taxonName.substr(0, 1).toUpperCase() + taxonName.substr(1).toLowerCase();
speciesAutoComplete($newTaxonFamilyInput, '&rank=family&taxonGroupId=' + currentSelectedTaxonGroup).then(value => {
$newTaxonFamilyIdInput.val(value);
})
$newTaxonNameInput.val(capitalizedTaxonName);
$newTaxonParentIdInput.val($newTaxonParent.val())
$taxonForm.show();
const authorAutoComplete = $('#author-auto-complete');
authorAutoComplete.empty();
authorAutoComplete.val(null).trigger('change');

$newTaxonParent.empty();
$newTaxonParent.val(null).trigger('change');
}

function addNewTaxonToObservedList(name, gbifKey, rank, taxaId = null, familyId = "", authorName = "") {
function addNewTaxonToObservedList(name, gbifKey, rank, taxaId = null, parentId = "", authorName = "") {
let postData = {
'gbifKey': gbifKey,
'taxonName': name,
'rank': rank,
'taxonGroupId': currentSelectedTaxonGroup,
'authorName': authorName
};
if (familyId) {
postData['familyId'] = familyId
if (parentId) {
postData['parentId'] = parentId
}
let table = $('.find-taxon-table');
table.hide();
Expand Down Expand Up @@ -149,18 +152,27 @@ export const addNewTaxon = (() => {
function handleAddNewTaxon(event) {
let $rank = $taxonForm.find('.new-taxon-rank');
let $author = $taxonForm.find('#author-auto-complete');
const familyId = $newTaxonFamilyIdInput.val();
if (!familyId) {
alert("Missing family");
const parentId = $newTaxonParent.val();
if (!parentId) {
alert("Missing parent");
return
}
addNewTaxonToObservedList($newTaxonNameInput.val(), '', $rank.val(), null, familyId, $author.val());
addNewTaxonToObservedList($newTaxonNameInput.val(), '', $rank.val(), null, parentId, $author.val());
$taxonForm.hide();
$newTaxonFamilyInput.val("")
$newTaxonFamilyIdInput.val("")
$newTaxonParentIdInput.val("")
}

function init(_selectedTaxonGroup) {

$('#addNewTaxonModal').on('shown.bs.modal', function (e) {
const authorAutoComplete = $('#author-auto-complete');
authorAutoComplete.empty();
authorAutoComplete.val(null).trigger('change');

$newTaxonParent.empty();
$newTaxonParent.val(null).trigger('change');
});
selectedTaxonGroup = _selectedTaxonGroup

$('#find-taxon-button').on('click', handleFindTaxonButton)
Expand Down
3 changes: 2 additions & 1 deletion bims/static/js/taxa_management/taxa_table.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ export const taxaTable = (() => {
data: function (params) {
return {
term: params.term,
rank: $(this).data('rank')
rank: $(this).data('rank'),
taxonGroupId: currentSelectedTaxonGroup
}
},
processResults: function (data) {
Expand Down
14 changes: 8 additions & 6 deletions bims/templates/taxa_management.html
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ <h5 class="modal-title">Add New Taxon</h5>
</tbody>
</table>
</div>
<div class="container new-taxon-form" style="margin-top: 15px; display: none">
<div class="container new-taxon-form" style="margin-top: 15px;">
<div class="alert alert-info" role="alert">
Whilst the system will accept taxa that are not listed in GBIF,
you should use the capability only for taxa where the classification is partly unresolved.
Expand All @@ -462,7 +462,7 @@ <h5 class="modal-title">Add New Taxon</h5>
<table class="table table-sm">
<thead>
<tr>
<th scope="col" style="width: 25%">Family</th>
<th scope="col" style="width: 25%">Parent</th>
<th scope="col" style="width: 25%">Taxon Name</th>
<th scope="col" style="width: 25%">Author</th>
<th scope="col" style="width: 25%">Rank</th>
Expand All @@ -472,8 +472,8 @@ <h5 class="modal-title">Add New Taxon</h5>
<tbody>
<tr>
<td>
<select name="newTaxonFamilyName" class="new-taxon-family-name form-control" data-add-new-tag="false" style="width: 100%"></select>
<input type="hidden" class="new-taxon-family-id">
<select name="newTaxonFamilyName" class="taxon-auto-complete form-control" id="new-taxon-parent" style="width: 100%"></select>
<input type="hidden" class="new-taxon-parent-id">
</td>
<td>
<input type="text" class="form-control" placeholder="Taxon Name" id="new-taxon-name">
Expand All @@ -486,8 +486,10 @@ <h5 class="modal-title">Add New Taxon</h5>
</td>
<td>
<select class="form-control new-taxon-rank">
{% for rank in taxon_rank %}
{% for rank in all_ranks %}
{% if rank != 'DOMAIN' %}
<option value="{{ rank }}" {% if rank == 'SPECIES' %}selected{% endif %}>{{ rank }}</option>
{% endif %}
{% endfor %}
</select>
</td>
Expand All @@ -500,7 +502,7 @@ <h5 class="modal-title">Add New Taxon</h5>
</tr>
<tr>
<td style="border-top-width: 0"><span class="text-muted" style="font-size: 10pt; font-style: italic">
This is an autocomplete search, if the family does not exist in the system, please add it first.</span>
This is an autocomplete search, if the taxon does not exist in the system, please add it first.</span>
</td>
</tr>
</tbody>
Expand Down
73 changes: 73 additions & 0 deletions bims/tests/test_add_new_taxon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from django.test import TestCase
from django.urls import reverse
from django_tenants.test.cases import FastTenantTestCase
from django_tenants.test.client import TenantClient
from rest_framework.test import APIClient
from unittest.mock import patch

from django.contrib.auth.models import User
from bims.tests.model_factories import TaxonomyF, TaxonGroupF, UserF, IUCNStatusF


def mock_update_taxonomy_from_gbif(key, fetch_parent=True, get_vernacular=True):
iucn_status = IUCNStatusF.create()
taxonomy = TaxonomyF.create(
gbif_key=key,
scientific_name="Mocked Scientific Name",
canonical_name="Mocked Canonical Name",
iucn_status=iucn_status
)
return taxonomy

class AddNewTaxonTestCase(FastTenantTestCase):
def setUp(self):
self.client = TenantClient(self.tenant)
self.user = UserF.create(username='testuser', password='testpassword')
self.client.login(username='testuser', password='testpassword')
self.taxon_group = TaxonGroupF.create(
name='test'
)
self.taxonomy = TaxonomyF()

@patch('bims.api_views.taxon.update_taxonomy_from_gbif', side_effect=mock_update_taxonomy_from_gbif)
def test_add_new_taxon_with_gbif_key(self, mock_update):
data = {
'gbifKey': '1',
'taxonName': 'Test Taxon',
'taxonGroup': self.taxon_group.name,
'rank': 'species',
'authorName': 'Test Author',
}
response = self.client.post(reverse('add-new-taxon'), data)
self.assertEqual(response.status_code, 200)
self.assertTrue('id' in response.data)
self.assertTrue('taxon_name' in response.data)
self.assertEqual(response.data['taxon_name'], 'Mocked Canonical Name')

@patch('bims.api_views.taxon.update_taxonomy_from_gbif', side_effect=mock_update_taxonomy_from_gbif)
def test_add_new_taxon_without_gbif_key(self, mock_update):
data = {
'taxonName': 'Test Taxon Without GBIF',
'taxonGroup': self.taxon_group.name,
'rank': 'species',
'parentId': self.taxonomy.id,
'authorName': 'Test Author Without GBIF',
}
response = self.client.post(reverse('add-new-taxon'), data)
self.assertEqual(response.status_code, 200)
self.assertTrue('id' in response.data)
self.assertTrue('taxon_name' in response.data)
self.assertEqual(response.data['taxon_name'], 'Test Taxon Without GBIF')

data = {
'taxonName': 'Test Taxon Without GBIF 2',
'taxonGroupId': self.taxon_group.id,
'rank': 'species',
'familyId': self.taxonomy.id,
'authorName': 'Test Author Without GBIF 2',
}
response = self.client.post(reverse('add-new-taxon'), data)
self.assertEqual(response.status_code, 200)
self.assertTrue('id' in response.data)
self.assertTrue('taxon_name' in response.data)
self.assertEqual(response.data['taxon_name'], 'Test Taxon Without GBIF 2')

0 comments on commit 722baac

Please sign in to comment.