Merge pull request 'js_tree_element_improvement' (#161) from js_tree_element_improvement into master
Reviewed-on: SGD-Nord/konova#161
This commit is contained in:
		
						commit
						21d1fabeec
					
				@ -65,24 +65,23 @@ class KonovaCode(models.Model):
 | 
			
		||||
            ret_val += ", " + self.parent.long_name
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            code (KonovaCode): The manipulated KonovaCode instance
 | 
			
		||||
        """
 | 
			
		||||
        if self.is_leaf:
 | 
			
		||||
            return None
 | 
			
		||||
            return self
 | 
			
		||||
 | 
			
		||||
        children = KonovaCode.objects.filter(
 | 
			
		||||
            code_lists__in=self.code_lists.all(),
 | 
			
		||||
            parent=self
 | 
			
		||||
        ).order_by(
 | 
			
		||||
            "long_name"
 | 
			
		||||
            order_by
 | 
			
		||||
        )
 | 
			
		||||
        self.children = children
 | 
			
		||||
        for child in children:
 | 
			
		||||
            child.add_children()
 | 
			
		||||
            child.add_children(order_by)
 | 
			
		||||
        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, \
 | 
			
		||||
    CODELIST_COMPENSATION_ACTION_DETAIL_ID
 | 
			
		||||
from compensation.models import CompensationDocument, EcoAccountDocument
 | 
			
		||||
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple
 | 
			
		||||
from intervention.inputs import CompensationActionTreeCheckboxSelectMultiple, \
 | 
			
		||||
    CompensationStateTreeRadioSelect
 | 
			
		||||
from konova.contexts import BaseContext
 | 
			
		||||
from konova.forms import BaseModalForm, NewDocumentModalForm, RemoveModalForm
 | 
			
		||||
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')?
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    biotope_type = forms.ModelChoiceField(
 | 
			
		||||
    biotope_type = forms.ChoiceField(
 | 
			
		||||
        label=_("Biotope Type"),
 | 
			
		||||
        label_suffix="",
 | 
			
		||||
        required=True,
 | 
			
		||||
        help_text=_("Select the biotope type"),
 | 
			
		||||
        queryset=KonovaCode.objects.filter(
 | 
			
		||||
            is_archived=False,
 | 
			
		||||
            is_leaf=True,
 | 
			
		||||
            code_lists__in=[CODELIST_BIOTOPES_ID],
 | 
			
		||||
        ),
 | 
			
		||||
        widget=autocomplete.ModelSelect2(
 | 
			
		||||
            url="codes-biotope-autocomplete",
 | 
			
		||||
            attrs={
 | 
			
		||||
                "data-placeholder": _("Biotope Type"),
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        widget=CompensationStateTreeRadioSelect(),
 | 
			
		||||
    )
 | 
			
		||||
    biotope_extra = forms.ModelMultipleChoiceField(
 | 
			
		||||
        label=_("Biotope additional type"),
 | 
			
		||||
@ -209,6 +200,16 @@ class NewStateModalForm(BaseModalForm):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("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):
 | 
			
		||||
        state = self.instance.add_state(self, is_before_state)
 | 
			
		||||
@ -271,8 +272,9 @@ class EditCompensationStateModalForm(NewStateModalForm):
 | 
			
		||||
        self.state = kwargs.pop("state", None)
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.form_title = _("Edit state")
 | 
			
		||||
        biotope_type_id = self.state.biotope_type.id if self.state.biotope_type else None
 | 
			
		||||
        form_data = {
 | 
			
		||||
            "biotope_type": self.state.biotope_type,
 | 
			
		||||
            "biotope_type": biotope_type_id,
 | 
			
		||||
            "biotope_extra": self.state.biotope_type_details.all(),
 | 
			
		||||
            "surface": self.state.surface,
 | 
			
		||||
        }
 | 
			
		||||
@ -280,7 +282,8 @@ class EditCompensationStateModalForm(NewStateModalForm):
 | 
			
		||||
 | 
			
		||||
    def save(self, is_before_state: bool = False):
 | 
			
		||||
        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.surface = self.cleaned_data.get("surface", None)
 | 
			
		||||
        state.save()
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,8 @@ Created on: 16.11.21
 | 
			
		||||
import shutil
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
 | 
			
		||||
from codelist.models import KonovaCode
 | 
			
		||||
from user.models import User, Team
 | 
			
		||||
from django.db import models, transaction
 | 
			
		||||
from django.db.models import QuerySet, Sum
 | 
			
		||||
@ -142,8 +144,10 @@ class AbstractCompensation(BaseObject, GeoReferencedMixin):
 | 
			
		||||
        """
 | 
			
		||||
        form_data = form.cleaned_data
 | 
			
		||||
        with transaction.atomic():
 | 
			
		||||
            biotope_type_id = form_data["biotope_type"]
 | 
			
		||||
            code = KonovaCode.objects.get(id=biotope_type_id)
 | 
			
		||||
            state = CompensationState.objects.create(
 | 
			
		||||
                biotope_type=form_data["biotope_type"],
 | 
			
		||||
                biotope_type=code,
 | 
			
		||||
                surface=form_data["surface"],
 | 
			
		||||
            )
 | 
			
		||||
            state_additional_types = form_data["biotope_extra"]
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
from django import forms
 | 
			
		||||
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):
 | 
			
		||||
@ -38,7 +38,17 @@ class TreeCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
 | 
			
		||||
    """ 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:
 | 
			
		||||
        abstract = True
 | 
			
		||||
@ -68,6 +78,30 @@ class KonovaCodeTreeCheckboxSelectMultiple(TreeCheckboxSelectMultiple):
 | 
			
		||||
        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):
 | 
			
		||||
    """ Provides multiple selection of CompensationActions
 | 
			
		||||
 | 
			
		||||
@ -80,3 +114,30 @@ class CompensationActionTreeCheckboxSelectMultiple(KonovaCodeTreeCheckboxSelectM
 | 
			
		||||
            "code_lists__in": [CODELIST_COMPENSATION_ACTION_ID],
 | 
			
		||||
            "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 %}
 | 
			
		||||
<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%}
 | 
			
		||||
        <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 %}
 | 
			
		||||
        {% fa5_icon 'angle-right' %}
 | 
			
		||||
        <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/checkbox-tree-select-content.html' %}
 | 
			
		||||
        {% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
 | 
			
		||||
        {% endwith %}
 | 
			
		||||
    </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
@ -5,7 +5,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div id="tree-root">
 | 
			
		||||
    {% include 'konova/widgets/checkbox-tree-select-content.html' %}
 | 
			
		||||
    {% include 'konova/widgets/tree/checkbox/checkbox-tree-select-content.html' %}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
@ -47,9 +47,12 @@
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
        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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user