Merge pull request 'js_tree_element_improvement' (#161) from js_tree_element_improvement into master

Reviewed-on: SGD-Nord/konova#161
pull/162/head
mpeltriaux 2 years ago
commit 21d1fabeec

@ -65,24 +65,23 @@ class KonovaCode(models.Model):
ret_val += ", " + self.parent.long_name ret_val += ", " + self.parent.long_name
return ret_val return ret_val
def add_children(self): def add_children(self, order_by: str = "long_name"):
""" Adds all children (resurcively until leaf) as .children to the KonovaCode """ Adds all children (resurcively until leaf) as .children to the KonovaCode
Returns: Returns:
code (KonovaCode): The manipulated KonovaCode instance code (KonovaCode): The manipulated KonovaCode instance
""" """
if self.is_leaf: if self.is_leaf:
return None return self
children = KonovaCode.objects.filter( children = KonovaCode.objects.filter(
code_lists__in=self.code_lists.all(),
parent=self parent=self
).order_by( ).order_by(
"long_name" order_by
) )
self.children = children self.children = children
for child in children: for child in children:
child.add_children() child.add_children(order_by)
return self return self

@ -17,7 +17,8 @@ from codelist.models import KonovaCode
from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \ from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_EXTRA_CODES_ID, \
CODELIST_COMPENSATION_ACTION_DETAIL_ID CODELIST_COMPENSATION_ACTION_DETAIL_ID
from compensation.models import CompensationDocument, EcoAccountDocument from compensation.models import CompensationDocument, EcoAccountDocument
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple, \
CompensationStateTreeRadioSelect
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm
from konova.models import DeadlineType from konova.models import DeadlineType
@ -156,22 +157,12 @@ class NewStateModalForm(BaseModalForm):
What has been on this area before changes/compensations have been applied and what will be the result ('after')? What has been on this area before changes/compensations have been applied and what will be the result ('after')?
""" """
biotope_type = forms.ModelChoiceField( biotope_type = forms.ChoiceField(
label=_("Biotope Type"), label=_("Biotope Type"),
label_suffix="", label_suffix="",
required=True, required=True,
help_text=_("Select the biotope type"), help_text=_("Select the biotope type"),
queryset=KonovaCode.objects.filter( widget=CompensationStateTreeRadioSelect(),
is_archived=False,
is_leaf=True,
code_lists__in=[CODELIST_BIOTOPES_ID],
),
widget=autocomplete.ModelSelect2(
url="codes-biotope-autocomplete",
attrs={
"data-placeholder": _("Biotope Type"),
}
),
) )
biotope_extra = forms.ModelMultipleChoiceField( biotope_extra = forms.ModelMultipleChoiceField(
label=_("Biotope additional type"), label=_("Biotope additional type"),
@ -209,6 +200,16 @@ class NewStateModalForm(BaseModalForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.form_title = _("New state") self.form_title = _("New state")
self.form_caption = _("Insert data for the new state") self.form_caption = _("Insert data for the new state")
choices = KonovaCode.objects.filter(
code_lists__in=[CODELIST_BIOTOPES_ID],
is_archived=False,
is_leaf=True,
).values_list("id", flat=True)
choices = [
(choice, choice)
for choice in choices
]
self.fields["biotope_type"].choices = choices
def save(self, is_before_state: bool = False): def save(self, is_before_state: bool = False):
state = self.instance.add_state(self, is_before_state) state = self.instance.add_state(self, is_before_state)
@ -271,8 +272,9 @@ class EditCompensationStateModalForm(NewStateModalForm):
self.state = kwargs.pop("state", None) self.state = kwargs.pop("state", None)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.form_title = _("Edit state") self.form_title = _("Edit state")
biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
form_data = { form_data = {
"biotope_type": self.state.biotope_type, "biotope_type": biotope_type_id,
"biotope_extra": self.state.biotope_type_details.all(), "biotope_extra": self.state.biotope_type_details.all(),
"surface": self.state.surface, "surface": self.state.surface,
} }
@ -280,7 +282,8 @@ class EditCompensationStateModalForm(NewStateModalForm):
def save(self, is_before_state: bool = False): def save(self, is_before_state: bool = False):
state = self.state state = self.state
state.biotope_type = self.cleaned_data.get("biotope_type", None) biotope_type_id = self.cleaned_data.get("biotope_type", None)
state.biotope_type = KonovaCode.objects.get(id=biotope_type_id)
state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", [])) state.biotope_type_details.set(self.cleaned_data.get("biotope_extra", []))
state.surface = self.cleaned_data.get("surface", None) state.surface = self.cleaned_data.get("surface", None)
state.save() state.save()

@ -8,6 +8,8 @@ Created on: 16.11.21
import shutil import shutil
from django.contrib import messages from django.contrib import messages
from codelist.models import KonovaCode
from user.models import User, Team from user.models import User, Team
from django.db import models, transaction from django.db import models, transaction
from django.db.models import QuerySet, Sum from django.db.models import QuerySet, Sum
@ -142,8 +144,10 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
""" """
form_data = form.cleaned_data form_data = form.cleaned_data
with transaction.atomic(): with transaction.atomic():
biotope_type_id = form_data["biotope_type"]
code = KonovaCode.objects.get(id=biotope_type_id)
state = CompensationState.objects.create( state = CompensationState.objects.create(
biotope_type=form_data["biotope_type"], biotope_type=code,
surface=form_data["surface"], surface=form_data["surface"],
) )
state_additional_types = form_data["biotope_extra"] state_additional_types = form_data["biotope_extra"]

@ -1,6 +1,6 @@
from django import forms from django import forms
from codelist.models import KonovaCode from codelist.models import KonovaCode
from codelist.settings import CODELIST_COMPENSATION_ACTION_ID from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID
class DummyFilterInput(forms.HiddenInput): class DummyFilterInput(forms.HiddenInput):
@ -38,7 +38,17 @@ class TreeCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
""" Provides multiple selection of parent-child data """ Provides multiple selection of parent-child data
""" """
template_name = "konova/widgets/checkbox-tree-select.html" template_name = "konova/widgets/tree/checkbox/checkbox-tree-select.html"
class meta:
abstract = True
class TreeRadioSelect(forms.RadioSelect):
""" Provides single selection of parent-child data
"""
template_name = "konova/widgets/tree/radio/radio-tree-select.html"
class meta: class meta:
abstract = True abstract = True
@ -68,6 +78,30 @@ class KonovaCodeTreeCheckboxSelectMultiple(TreeCheckboxSelectMultiple):
return context return context
class KonovaCodeTreeRadioSelect(TreeRadioSelect):
""" Provides single selection of KonovaCode
"""
filter = None
def __init__(self, *args, **kwargs):
self.code_list = kwargs.pop("code_list", None)
self.filter = kwargs.pop("filter", {})
super().__init__(*args, **kwargs)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
codes = KonovaCode.objects.filter(
**self.filter,
)
codes = [
parent_code.add_children()
for parent_code in codes
]
context["codes"] = codes
return context
class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectMultiple): class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectMultiple):
""" Provides multiple selection of CompensationActions """ Provides multiple selection of CompensationActions
@ -80,3 +114,30 @@ class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectM
"code_lists__in": [CODELIST_COMPENSATION_ACTION_ID], "code_lists__in": [CODELIST_COMPENSATION_ACTION_ID],
"parent": None, "parent": None,
} }
class CompensationStateTreeRadioSelect(KonovaCodeTreeRadioSelect):
""" Provides single selection of CompensationState
"""
filter = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.filter = {
"code_lists__in": [CODELIST_BIOTOPES_ID],
"parent": None,
"is_archived": False,
}
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
codes = KonovaCode.objects.filter(
**self.filter,
)
codes = [
parent_code.add_children("short_name")
for parent_code in codes
]
context["codes"] = codes
return context

@ -263,3 +263,12 @@ Similar to bootstraps 'shadow-lg'
} }
*/ */
.collapse-icn > i{
transition: all 0.3s ease;
}
.collapsed .collapse-icn > i{
transform: rotate(-90deg);
}
.tree-label.badge{
font-size: 90%;
}

@ -2,18 +2,23 @@
{% for code in codes %} {% for code in codes %}
<div class="ml-4 tree-element"> <div class="ml-4 tree-element">
<label class="tree-label" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}"> <label class="tree-label collapsed" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}">
{% if code.is_leaf%} {% if code.is_leaf%}
<input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="checkbox" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/> <input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="checkbox" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/>
{% else %} {% else %}
{% fa5_icon 'angle-right' %} <span class="collapse-icn">
{% fa5_icon 'angle-down' %}
</span>
{% endif %}
{% if code.short_name %}
({{code.short_name}})
{% endif %} {% endif %}
{{code.long_name}} {{code.long_name}}
</label> </label>
{% if not code.is_leaf %} {% if not code.is_leaf %}
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children"> <div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
{% with code.children as codes %} {% with code.children as codes %}
{% include 'konova/widgets/checkbox-tree-select-content.html' %} {% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
{% endwith %} {% endwith %}
</div> </div>
{% endif %} {% endif %}

@ -5,7 +5,7 @@
</div> </div>
<div id="tree-root"> <div id="tree-root">
{% include 'konova/widgets/checkbox-tree-select-content.html' %} {% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
</div> </div>
<script> <script>
@ -47,9 +47,12 @@
} }
); );
if(val.length > 0){ if(val.length > 0){
// Hide everything
allTreeElements.hide() allTreeElements.hide()
// Now show again everything matching the query
allTreeElementsContain.show() allTreeElementsContain.show()
}else{ }else{
// Show everything if no query exists
allTreeElements.show() allTreeElements.show()
} }
} }

@ -0,0 +1,25 @@
{% load l10n fontawesome_5 %}
{% for code in codes %}
<div class="ml-4 tree-element">
<label class="tree-label collapsed" role="{% if not code.is_leaf%}button{% endif %}" for="input_{{code.pk|unlocalize}}" id="{{code.pk|unlocalize}}" data-toggle="collapse" data-target="#children_{{code.pk|unlocalize}}" aria-expanded="true" aria-controls="children_{{code.pk|unlocalize}}">
{% if code.is_leaf%}
<input class="tree-input" id="input_{{code.pk|unlocalize}}" name="{{ widget.name }}" type="radio" value="{{code.pk|unlocalize}}" {% if code.pk|unlocalize in widget.value %}checked{% endif %}/>
{% else %}
<span class="collapse-icn">
{% fa5_icon 'angle-down' %}
</span>
{% endif %}
{% if code.short_name %}
({{code.short_name}})
{% endif %}
{{code.long_name}}
</label>
{% if not code.is_leaf %}
<div id="children_{{code.pk|unlocalize}}" data-toggle="collapse" class="collapse tree-element-children">
{% with code.children as codes %}
{% include 'konova/widgets/tree/radio/radio-tree-select-content.html' %}
{% endwith %}
</div>
{% endif %}
</div>
{% endfor %}

@ -0,0 +1,62 @@
{% load i18n %}
<div class="ml-4 mb-4">
<input id="tree-search-input" class="form-control" type="text" placeholder="{% trans 'Search' %}"/>
</div>
<div id="tree-root">
{% include 'konova/widgets/tree/radio/radio-tree-select-content.html' %}
</div>
<script>
function toggleSelectedCssClass(element){
element = $(element);
var cssClass = "badge rlp-r"
// Find all already tagged input elements and reset them to be untagged
var allTaggedInputs = $("#tree-root").find(".badge.rlp-r")
allTaggedInputs.removeClass(cssClass)
// Find all parents of selected element
var parentElements = element.parents(".tree-element-children")
// Tag parents of element
var parentLabels = parentElements.siblings(".tree-label");
parentLabels.addClass(cssClass);
}
function changeHandler(event){
toggleSelectedCssClass(this);
}
function searchInputHandler(event){
var elem = $(this);
var val = elem.val()
var allTreeElements = $(".tree-element")
var allTreeElementsContain = $(".tree-element").filter(function(){
var reg = new RegExp(val, "i");
return reg.test($(this).text());
}
);
if(val.length > 0){
// Hide everything
allTreeElements.hide()
// Now show again everything matching the query
allTreeElementsContain.show()
}else{
// Show everything if no query exists
allTreeElements.show()
}
}
// Add event listener on search input
$("#tree-search-input").keyup(searchInputHandler)
// Add event listener on changed checkboxes
$(".tree-input").change(changeHandler);
// initialize all pre-checked checkboxes (e.g. on an edit form)
var preCheckedElements = $(".tree-input:checked");
preCheckedElements.each(function (index, element){
toggleSelectedCssClass(element);
})
</script>
Loading…
Cancel
Save