Skip to content

Commit

Permalink
Fix add new taxon (#4060)
Browse files Browse the repository at this point in the history
  • Loading branch information
dimasciput authored Jul 11, 2024
1 parent c7b6f27 commit 34eb928
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 128 deletions.
2 changes: 1 addition & 1 deletion bims/api_views/taxon_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def create_taxon_proposal(taxon, taxon_group, data={}, iucn_status=None, endemis
if data.get('tags'):
proposal.tags.set(data.get('tags'))
else:
proposal.tags.set(taxon.tags.all())
proposal.tags.clear()
proposal.biographic_distributions.set(taxon.biographic_distributions.all())
proposal.save()

Expand Down
201 changes: 117 additions & 84 deletions bims/models/taxonomy.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,90 +238,6 @@ class AbstractTaxonomy(AbstractValidation):
class Meta:
abstract = True


class Taxonomy(AbstractTaxonomy):
CATEGORY_CHOICES = (
(ORIGIN_CATEGORIES['non-native'], 'Non-Native'),
(ORIGIN_CATEGORIES['native'], 'Native'),
(ORIGIN_CATEGORIES['unknown'], 'Unknown'),
(ORIGIN_CATEGORIES['non-native: invasive'], 'Non-native: invasive'),
(
ORIGIN_CATEGORIES['non-native: non-invasive'],
'Non-native: non-invasive'
)
)
CATEGORY_CHOICES_DICT = {
ORIGIN_CATEGORIES['non-native']: 'Non-Native',
ORIGIN_CATEGORIES['native']: 'Native',
ORIGIN_CATEGORIES['unknown']: 'Unknown',
ORIGIN_CATEGORIES['non-native: invasive']: 'Non-native: invasive',
ORIGIN_CATEGORIES['non-native: non-invasive']: 'Non-native: non-invasive'
}

def save_json_data(self, json_field):
max_allowed = 10
attempt = 0
is_dictionary = False
json_data = {}
if not json_field:
return json_data
while not is_dictionary and attempt < max_allowed:
if not json_field:
break
if isinstance(json_field, dict):
is_dictionary = True
json_data = json_field
else:
json_data = json.loads(json_field)
attempt += 1
return json_data

# noinspection PyClassicStyleClass
class Meta:
"""Meta class for project."""
app_label = 'bims'
verbose_name_plural = 'Taxa'
verbose_name = 'Taxonomy'

def __unicode__(self):
return '%s - %s' % (
self.scientific_name,
self.rank
)

def __str__(self):
return '%s - %s' % (
self.scientific_name,
self.rank
)

def get_direct_children(self):
children = Taxonomy.objects.filter(
parent=self
)
return children

def get_all_children(self):
query = {}
parent = ''
or_condition = models.Q()
for i in range(6): # species to class
parent += 'parent__'
query[parent + 'in'] = [self]
for key, value in query.items():
or_condition |= models.Q(**{key: value})
return Taxonomy.objects.filter(or_condition)

def parent_by_rank(self, rank):
taxon = self
current_rank = taxon.rank
while current_rank != rank and taxon.parent:
taxon = taxon.parent
current_rank = taxon.rank
if current_rank == rank:
return taxon
return None

@property
def data_name(self):
return self.canonical_name
Expand Down Expand Up @@ -421,6 +337,123 @@ def name(self):
return self.scientific_name
return '-'


class Taxonomy(AbstractTaxonomy):
CATEGORY_CHOICES = (
(ORIGIN_CATEGORIES['non-native'], 'Non-Native'),
(ORIGIN_CATEGORIES['native'], 'Native'),
(ORIGIN_CATEGORIES['unknown'], 'Unknown'),
(ORIGIN_CATEGORIES['non-native: invasive'], 'Non-native: invasive'),
(
ORIGIN_CATEGORIES['non-native: non-invasive'],
'Non-native: non-invasive'
)
)
CATEGORY_CHOICES_DICT = {
ORIGIN_CATEGORIES['non-native']: 'Non-Native',
ORIGIN_CATEGORIES['native']: 'Native',
ORIGIN_CATEGORIES['unknown']: 'Unknown',
ORIGIN_CATEGORIES['non-native: invasive']: 'Non-native: invasive',
ORIGIN_CATEGORIES['non-native: non-invasive']: 'Non-native: non-invasive'
}

def save_json_data(self, json_field):
max_allowed = 10
attempt = 0
is_dictionary = False
json_data = {}
if not json_field:
return json_data
while not is_dictionary and attempt < max_allowed:
if not json_field:
break
if isinstance(json_field, dict):
is_dictionary = True
json_data = json_field
else:
json_data = json.loads(json_field)
attempt += 1
return json_data

# noinspection PyClassicStyleClass
class Meta:
"""Meta class for project."""
app_label = 'bims'
verbose_name_plural = 'Taxa'
verbose_name = 'Taxonomy'

def __unicode__(self):
return '%s - %s' % (
self.scientific_name,
self.rank
)

def __str__(self):
return '%s - %s' % (
self.scientific_name,
self.rank
)

def get_direct_children(self):
children = Taxonomy.objects.filter(
parent=self
)
return children

def get_all_children(self):
query = {}
parent = ''
or_condition = models.Q()
for i in range(6): # species to class
parent += 'parent__'
query[parent + 'in'] = [self]
for key, value in query.items():
or_condition |= models.Q(**{key: value})
return Taxonomy.objects.filter(or_condition)

def parent_by_rank(self, rank):
taxon = self
current_rank = taxon.rank
while current_rank != rank and taxon.parent:
taxon = taxon.parent
current_rank = taxon.rank
if current_rank == rank:
return taxon
return None


def save(self, *args, **kwargs):
update_taxon_with_gbif = False
if self.gbif_data:
self.gbif_data = self.save_json_data(self.gbif_data)
if self.additional_data:
self.additional_data = self.save_json_data(self.additional_data)
if self.additional_data and 'fetch_gbif' in self.additional_data:
update_taxon_with_gbif = True
del self.additional_data['fetch_gbif']
if not self.hierarchical_data:
self.hierarchical_data = {
'family_name': self.get_taxon_rank_name(TaxonomicRank.FAMILY.name),
'genus_name': self.get_taxon_rank_name(TaxonomicRank.GENUS.name),
'species_name': self.get_taxon_rank_name(TaxonomicRank.SPECIES.name),
}
elif 'family_name' not in self.hierarchical_data:
self.hierarchical_data['family_name'] = self.get_taxon_rank_name(TaxonomicRank.FAMILY.name)
elif 'species_name' not in self.hierarchical_data:
self.hierarchical_data['species_name'] = self.get_taxon_rank_name(TaxonomicRank.SPECIES.name)
elif 'genus_name' not in self.hierarchical_data:
self.hierarchical_data['genus_name'] = self.get_taxon_rank_name(TaxonomicRank.GENUS.name)

super(Taxonomy, self).save(*args, **kwargs)

if update_taxon_with_gbif:
from bims.utils.fetch_gbif import fetch_all_species_from_gbif
fetch_all_species_from_gbif(
species=self.scientific_name,
parent=self.parent,
gbif_key=self.gbif_key,
fetch_vernacular_names=True)

def send_new_taxon_email(self, taxon_group_id=None):
from bims.models import TaxonGroup
from bims.tasks.send_notification import send_mail_notification
Expand Down
1 change: 1 addition & 0 deletions bims/models/taxonomy_update_proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ def approve(self, reviewer: settings.AUTH_USER_MODEL):
'origin']
for field in fields_to_update:
if field == 'tags':
self.original_taxonomy.tags.clear()
self.original_taxonomy.tags.set(getattr(self, field).all())
else:
setattr(
Expand Down
83 changes: 48 additions & 35 deletions bims/static/js/non_requirejs/helpers.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,50 @@
const speciesAutoComplete = (inputDiv, additionalFilters = "") => {
return new Promise(function (resolve, reject) {
inputDiv.autocomplete({
source: function (request, response) {
$.ajax({
url: (
`/species-autocomplete/?` +
`term=${encodeURIComponent(request.term)}` +
`${additionalFilters}`
),
type: 'get',
dataType: 'json',
success: function (data) {
response($.map(data, function (item) {
return {
label: item.species,
value: item.id
}
}));
}
});

const parseAdditionalFilters = (filterString) => {
const params = new URLSearchParams(filterString);
let filterObject = {};
for (const [key, value] of params.entries()) {
filterObject[key] = value;
}
return filterObject;
};
return new Promise((resolve, reject) => {
// Ensure inputDiv is a jQuery object
const $inputDiv = $(inputDiv);

if (typeof $inputDiv.select2 !== 'function') {
console.error('Select2 is not loaded.');
return reject(new Error('Select2 is not loaded.'));
}

const filters = parseAdditionalFilters(additionalFilters);

$inputDiv.select2({
ajax: {
url: '/species-autocomplete/',
dataType: 'json',
delay: 250,
data: (params) => ({
term: params.term,
...filters
}),
processResults: (data) => ({
results: data.map(item => ({
id: item.id,
text: item.species
}))
}),
cache: true
},
minLength: 3,
open: function (event, ui) {
setTimeout(function () {
$('.ui-autocomplete').css('z-index', 9999)
}, 0);
},
select: function (e, u) {
e.preventDefault();
console.log('clicked');
inputDiv.val(u.item.label)
resolve(u.item.value)
}
})
})
}
minimumInputLength: 3,
templateResult: (item) => item.text,
templateSelection: (item) => item.text
}).on('select2:select', (e) => {
resolve(e.params.data.id);
}).on('select2:open', () => {
setTimeout(() => {
$('.select2-results').css('z-index', 9999);
}, 0);
});
});
};
2 changes: 1 addition & 1 deletion bims/static/js/taxa_management/add_new_taxon.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const addNewTaxon = (() => {

function showNewTaxonForm(taxonName) {
let capitalizedTaxonName = taxonName.substr(0, 1).toUpperCase() + taxonName.substr(1).toLowerCase();
speciesAutoComplete($newTaxonFamilyInput, '&rank=family').then(value => {
speciesAutoComplete($newTaxonFamilyInput, '&rank=family&taxonGroupId=' + selectedTaxonGroup).then(value => {
$newTaxonFamilyIdInput.val(value);
})
$newTaxonNameInput.val(capitalizedTaxonName);
Expand Down
10 changes: 9 additions & 1 deletion bims/templates/edit_taxon.html
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,15 @@ <h2>
<div class="col-sm-10">
<select name="iucn_status" id="iucn_status" class="form-control form-control-sm">
{% for iucn_status in iucn_status_choices %}
<option value="{{ iucn_status.id }}" {% if iucn_status == object.iucn_status %}selected{% endif %}>{{ iucn_status.get_status }}</option>
<option value="{{ iucn_status.id }}"
{% if object.iucn_status %}
{% if iucn_status == object.iucn_status %}selected{% endif %}
{% else %}
{% if iucn_status.category == 'NE' %}
selected
{% endif %}
{% endif %}
>{{ iucn_status.get_status }}</option>
{% endfor %}
</select>
</div>
Expand Down
12 changes: 8 additions & 4 deletions bims/templates/taxa_management.html
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,13 @@ <h5 class="modal-title">Add New Taxon</h5>
</thead>
<tbody>
<tr>
<td><input type="text" class="form-control new-taxon-family-name" placeholder="Family">
<input type="hidden" class="new-taxon-family-id"></td>
<td><input type="text" class="form-control" placeholder="Taxon Name" id="new-taxon-name"></td>
<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">
</td>
<td>
<input type="text" class="form-control" placeholder="Taxon Name" id="new-taxon-name">
</td>
<td>

<div>
Expand Down Expand Up @@ -773,7 +777,7 @@ <h5 class="modal-title" id="statusModalLabel">Status</h5>
</script>
<script src="{% static "js/libs/jquery/jquery-3.3.1.min.js" %}"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>
<script src="{% static "js/libs/jquery-ui-1.12.1/jquery-ui.min.js" %}"></script>
<script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>

<script src="https://cdn.datatables.net/v/dt/jq-3.7.0/dt-2.0.8/fc-5.0.1/fh-4.0.1/datatables.min.js"></script>
<script src="{% static "library/popper.js/1.11.0/popper.min.js" %}" crossorigin="anonymous"></script>
Expand Down
5 changes: 4 additions & 1 deletion bims/templatetags/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,7 @@ def is_fada_site():

@register.filter
def get_attr(obj, attr_name):
return getattr(obj, attr_name)
try:
return getattr(obj, attr_name)
except AttributeError:
return ''
Loading

0 comments on commit 34eb928

Please sign in to comment.