From 77b290216b49cad2701fd6e9af70f80f56c2abdd Mon Sep 17 00:00:00 2001 From: mipel Date: Thu, 26 Aug 2021 12:45:48 +0200 Subject: [PATCH] Konova Codelist enhancements * adds proper boolean mapping to update_codelist * differs between id and atom_id for KonovaCode since atomIds are not unique (could change in the future) * adds is_selectable and is_archived to KonovaCode * scales width of DAL form fields to 100% width * adds table-responsive wrapping container for table forms to prevent unwanted rendering artifacts in case of table resizing due to long content * adds autocomplete routes for law, registration offices and conservation offices --- codelist/admin.py | 6 ++- .../management/commands/update_codelist.py | 50 +++++-------------- codelist/models.py | 22 ++++++-- compensation/forms.py | 9 ++-- konova/autocompletes.py | 33 +++++++++++- konova/static/css/konova.css | 8 +++ konova/urls.py | 6 ++- templates/form/generic_table_form_body.html | 46 +++++++++-------- 8 files changed, 107 insertions(+), 73 deletions(-) diff --git a/codelist/admin.py b/codelist/admin.py index 5ca3fd78..f306b041 100644 --- a/codelist/admin.py +++ b/codelist/admin.py @@ -16,17 +16,21 @@ class KonovaCodeListAdmin(admin.ModelAdmin): class KonovaCodeAdmin(admin.ModelAdmin): list_display = [ "id", + "atom_id", "parent", "short_name", "long_name", "is_leaf", - "is_active", + "is_selectable", + "is_archived", ] readonly_fields = [ "id", "short_name", "long_name", + "is_archived", + "is_selectable", "is_leaf", "parent", ] diff --git a/codelist/management/commands/update_codelist.py b/codelist/management/commands/update_codelist.py index f7235ffd..808d1ed9 100644 --- a/codelist/management/commands/update_codelist.py +++ b/codelist/management/commands/update_codelist.py @@ -15,6 +15,10 @@ from codelist.settings import CODELIST_INTERVENTION_HANDLER_ID, CODELIST_CONSERV CODELIST_COMPENSATION_ACTION_ID, CODELIST_COMPENSATION_ACTION_CLASS_ID, CODELIST_COMPENSATION_ADDITIONAL_TYPE_ID, \ CODELIST_COMPENSATION_COMBINATION_ID, CODELIST_BASE_URL +bool_map = { + "true": True, + "false": False, +} class Command(BaseCommand): help = "Performs test on collisions using the identifier generation" @@ -56,41 +60,6 @@ class Command(BaseCommand): code_list=code_list, parent=None, ) - """ - for element in items: - atom_id = element.find("atomid").text - parent = element.find("vaterid").text - short_name = element.find("shortname").text - long_name = element.find("longname").text - is_archived = bool(element.find("archive").text) - - # If a parent has been set, we need to fetch/create this entry. Otherwise ("0") we ignore it. - if parent == "0": - parent = None - else: - parent = KonovaCode.objects.get_or_create( - id=parent, - )[0] - - code = KonovaCode.objects.get_or_create( - id=atom_id, - ) - created = code[1] - if created: - num_created += 1 - else: - num_updated += 1 - code = code[0] - code.short_name = short_name - code.long_name = long_name - code.parent = parent - code.is_active = is_archived - - code.save() - if code not in code_list.codes.all(): - code_list.codes.add(code) - - """ except KeyboardInterrupt: self._break_line() @@ -102,19 +71,24 @@ class Command(BaseCommand): else: for element in items: children = element.find("items") + _id = element.find("id").text atom_id = element.find("atomid").text + selectable = element.find("selectable").text.lower() + selectable = bool_map.get(selectable, False) short_name = element.find("shortname").text long_name = element.find("longname").text - is_archived = bool(element.find("archive").text) + is_archived = bool_map.get((element.find("archive").text.lower()), False) code = KonovaCode.objects.get_or_create( - id=atom_id, + id=_id, ) code = code[0] + code.atom_id = atom_id code.short_name = short_name code.long_name = long_name code.parent = parent - code.is_active = is_archived + code.is_selectable = selectable + code.is_archived = is_archived code.is_leaf = children is None code.save() diff --git a/codelist/models.py b/codelist/models.py index c1d0c9f4..fabb6f03 100644 --- a/codelist/models.py +++ b/codelist/models.py @@ -10,7 +10,12 @@ class KonovaCode(models.Model): """ id = models.IntegerField( primary_key=True, - help_text="AtomId; Identifies this code uniquely over all NatIT projects" + help_text="Regular Id" + ) + atom_id = models.IntegerField( + help_text="AtomId; Identifies this code uniquely over all NatIT projects; Duplicates possible", + null=True, + blank=True, ) parent = models.ForeignKey( "KonovaCode", @@ -30,20 +35,27 @@ class KonovaCode(models.Model): blank=True, help_text="", ) + is_selectable = models.BooleanField( + default=False, + help_text="Whether this code shall be used for any select actions or not" + ) is_leaf = models.BooleanField( default=False, help_text="Whether this code has children or not" ) - is_active = models.BooleanField( + is_archived = models.BooleanField( default=False, help_text="Whether this code is archived or not" ) def __str__(self): + ret_val = "" + if self.parent: + ret_val += self.parent.long_name + " > " + ret_val += self.long_name if self.short_name: - return "{} ({})".format(self.long_name, self.short_name) - else: - return self.long_name + ret_val += " ({})".format(self.short_name) + return ret_val class KonovaCodeList(models.Model): diff --git a/compensation/forms.py b/compensation/forms.py index 647ca342..c0709975 100644 --- a/compensation/forms.py +++ b/compensation/forms.py @@ -16,7 +16,7 @@ from django.utils.translation import gettext_lazy as _ from django.utils.translation import pgettext_lazy as _con from codelist.models import KonovaCode -from codelist.settings import CODELIST_BIOTOPES_ID +from codelist.settings import CODELIST_BIOTOPES_ID, CODELIST_COMPENSATION_ACTION_ID from compensation.models import Payment, CompensationState, CompensationAction, UnitChoices from konova.contexts import BaseContext from konova.forms import BaseForm, BaseModalForm @@ -106,7 +106,7 @@ class NewStateModalForm(BaseModalForm): required=True, help_text=_("Select the biotope type"), queryset=KonovaCode.objects.filter( - is_active=True, + is_archived=False, is_leaf=True, code_lists__in=[CODELIST_BIOTOPES_ID], ), @@ -278,13 +278,14 @@ class NewActionModalForm(BaseModalForm): required=True, help_text=_("Select the action type"), queryset=KonovaCode.objects.filter( - is_active=True, + is_archived=False, + is_leaf=True, + code_lists__in=[CODELIST_COMPENSATION_ACTION_ID], ), widget=autocomplete.ModelSelect2( url="codes-compensation-action-autocomplete", attrs={ "data-placeholder": _("Action"), - "data-class": "w-100", } ), ) diff --git a/konova/autocompletes.py b/konova/autocompletes.py index 4a092741..227b5923 100644 --- a/konova/autocompletes.py +++ b/konova/autocompletes.py @@ -9,7 +9,8 @@ from dal_select2.views import Select2QuerySetView from django.db.models import Q from codelist.models import KonovaCode -from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID +from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES_ID, CODELIST_LAW_ID, \ + CODELIST_REGISTRATION_OFFICE_ID, CODELIST_CONSERVATION_OFFICE_ID from compensation.models import EcoAccount from intervention.models import Intervention from organisation.models import Organisation @@ -92,7 +93,8 @@ class KonovaCodeAutocomplete(Select2QuerySetView): if self.request.user.is_anonymous: return KonovaCode.objects.none() qs = KonovaCode.objects.filter( - is_active=True, + is_archived=False, + is_selectable=True, is_leaf=True, ).order_by( "long_name" @@ -125,3 +127,30 @@ class BiotopeCodeAutocomplete(KonovaCodeAutocomplete): def __init__(self, *args, **kwargs): self.c = CODELIST_BIOTOPES_ID super().__init__(*args, **kwargs) + + +class LawCodeAutocomplete(KonovaCodeAutocomplete): + """ + Due to limitations of the django dal package, we need to subclass for each code list + """ + def __init__(self, *args, **kwargs): + self.c = CODELIST_LAW_ID + super().__init__(*args, **kwargs) + + +class RegistrationOfficeCodeAutocomplete(KonovaCodeAutocomplete): + """ + Due to limitations of the django dal package, we need to subclass for each code list + """ + def __init__(self, *args, **kwargs): + self.c = CODELIST_REGISTRATION_OFFICE_ID + super().__init__(*args, **kwargs) + + +class ConservationOfficeCodeAutocomplete(KonovaCodeAutocomplete): + """ + Due to limitations of the django dal package, we need to subclass for each code list + """ + def __init__(self, *args, **kwargs): + self.c = CODELIST_CONSERVATION_OFFICE_ID + super().__init__(*args, **kwargs) diff --git a/konova/static/css/konova.css b/konova/static/css/konova.css index c3854362..e54f9d1b 100644 --- a/konova/static/css/konova.css +++ b/konova/static/css/konova.css @@ -203,4 +203,12 @@ input:focus, textarea:focus, select:focus{ .scroll-300{ max-height: 300px; overflow: auto; +} + +/* +Extends css for django autocomplete light (dal) +No other approach worked to get the autocomplete fields to full width of parent containers +*/ +.select2-container{ + width: 100% !important; } \ No newline at end of file diff --git a/konova/urls.py b/konova/urls.py index 99014f39..32173a85 100644 --- a/konova/urls.py +++ b/konova/urls.py @@ -18,7 +18,8 @@ from django.contrib import admin from django.urls import path, include from konova.autocompletes import OrganisationAutocomplete, NonOfficialOrganisationAutocomplete, EcoAccountAutocomplete, \ - InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete + InterventionAutocomplete, CompensationActionCodeAutocomplete, BiotopeCodeAutocomplete, LawCodeAutocomplete, \ + RegistrationOfficeCodeAutocomplete, ConservationOfficeCodeAutocomplete from konova.settings import SSO_SERVER, SSO_PUBLIC_KEY, SSO_PRIVATE_KEY, DEBUG from konova.sso.sso import KonovaSSOClient from konova.views import logout_view, home_view, get_document_view, remove_document_view, remove_deadline_view @@ -51,6 +52,9 @@ urlpatterns = [ path("atcmplt/interventions", InterventionAutocomplete.as_view(), name="interventions-autocomplete"), path("atcmplt/codes/compensation-action", CompensationActionCodeAutocomplete.as_view(), name="codes-compensation-action-autocomplete"), path("atcmplt/codes/biotope", BiotopeCodeAutocomplete.as_view(), name="codes-biotope-autocomplete"), + path("atcmplt/codes/law", LawCodeAutocomplete.as_view(), name="codes-law-autocomplete"), + path("atcmplt/codes/reg-off", RegistrationOfficeCodeAutocomplete.as_view(), name="codes-registration-office-autocomplete"), + path("atcmplt/codes/cons-off", ConservationOfficeCodeAutocomplete.as_view(), name="codes-conservation-office-autocomplete"), ] if DEBUG: diff --git a/templates/form/generic_table_form_body.html b/templates/form/generic_table_form_body.html index 4b46e248..dbd6290a 100644 --- a/templates/form/generic_table_form_body.html +++ b/templates/form/generic_table_form_body.html @@ -1,23 +1,25 @@ {% load i18n %} - - - {% for field in form %} - - - - - {% endfor %} - -
- - {{ field.help_text }} - - {{ field }} - {% for error in field.errors %} -
- {{ error }} - {% endfor %} -
-{% if form.has_required_fields %} -{% trans 'Fields with * are required.' %} -{% endif %} \ No newline at end of file +
+ + + {% for field in form %} + + + + + {% endfor %} + +
+ + {{ field.help_text }} + + {{ field }} + {% for error in field.errors %} +
+ {{ error }} + {% endfor %} +
+ {% if form.has_required_fields %} + {% trans 'Fields with * are required.' %} + {% endif %} +
\ No newline at end of file