diff options
| author | Étienne Loks <etienne.loks@iggdrasil.net> | 2025-10-15 19:01:43 +0200 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2025-10-24 18:32:21 +0200 | 
| commit | d949976a9fb9ecf1e366486e604c89c56c02c1f8 (patch) | |
| tree | d52afb87c0b633f28c3f5e58388ac9ecbb40d9ee | |
| parent | c08bab38239171e535e51a2f71230c37d6e84985 (diff) | |
| download | Ishtar-d949976a9fb9ecf1e366486e604c89c56c02c1f8.tar.bz2 Ishtar-d949976a9fb9ecf1e366486e604c89c56c02c1f8.zip | |
♻️  django 3.2 - new version of libraries: fix errors and deprecation warnings
| -rw-r--r-- | Makefile.example | 14 | ||||
| -rw-r--r-- | archaeological_files/models.py | 12 | ||||
| -rw-r--r-- | archaeological_finds/models_finds.py | 2 | ||||
| -rw-r--r-- | archaeological_operations/models.py | 12 | ||||
| -rw-r--r-- | docs/generate_values_doc.py | 1 | ||||
| -rw-r--r-- | example_project/settings.py | 1 | ||||
| -rw-r--r-- | ishtar_common/admin.py | 13 | ||||
| -rw-r--r-- | ishtar_common/data_importer.py | 3 | ||||
| -rw-r--r-- | ishtar_common/jinja_filters.py | 2 | ||||
| -rw-r--r-- | ishtar_common/models.py | 8 | ||||
| -rw-r--r-- | ishtar_common/models_common.py | 5 | ||||
| -rw-r--r-- | ishtar_common/models_imports.py | 11 | ||||
| -rw-r--r-- | ishtar_common/utils.py | 24 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 10 | ||||
| -rw-r--r-- | ishtar_common/widgets.py | 6 | 
15 files changed, 67 insertions, 57 deletions
| diff --git a/Makefile.example b/Makefile.example index aba1818d3..aa49d27ec 100644 --- a/Makefile.example +++ b/Makefile.example @@ -123,21 +123,21 @@ test_verbose: clean  ## launch tests with verbose  	cd $(project); $(PYTHON) manage.py test -v 2 $(apps)  soft_test: clean  ## launch tests without database regeneration -	cd $(project); $(PYTHON) manage.py test -k --tag gis --exclude-tag ui --exclude-tag libreoffice $(apps) -	cd $(project); $(PYTHON) manage.py test -k --exclude-tag gis --exclude-tag ui $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --tag gis --exclude-tag ui --exclude-tag libreoffice $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --exclude-tag gis --exclude-tag ui $(apps)  soft_test_verbose: clean  ## launch tests without database regeneration - verbose -	cd $(project); $(PYTHON) manage.py test -k --verbosity 2 $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --verbosity 2 $(apps)  soft_test_multi: clean  ## launch multi-process tests without database regeneration -	cd $(project); $(PYTHON) manage.py test -k --parallel $(NB_PROCESS) --exclude-tag libreoffice --exclude-tag ui --tag gis $(apps) -	cd $(project); $(PYTHON) manage.py test -k --parallel $(NB_PROCESS) --exclude-tag libreoffice --exclude-tag ui --exclude-tag gis $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --parallel $(NB_PROCESS) --exclude-tag libreoffice --exclude-tag ui --tag gis $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --parallel $(NB_PROCESS) --exclude-tag libreoffice --exclude-tag ui --exclude-tag gis $(apps)  test_ui: clean  ## launch tests for UI  	cd $(project); $(PYTHON) manage.py test --tag ui $(apps)  soft_test_ui: clean  ## launch soft tests for UI -	cd $(project); $(PYTHON) manage.py test -k --tag ui $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --tag ui $(apps)  build_gitlab: clean collectstatic makemessages  ## specific build for gitlab  	cd $(project); $(PYTHON) ./manage.py migrate @@ -146,7 +146,7 @@ test_gitlab: clean collectstatic makemessages ## specific test for gitlab  	cd $(project); $(PYTHON) manage.py test --exclude-tag no_ci --exclude-tag gis --exclude-tag ui --exclude-tag libreoffice $(apps)  soft_test_gitlab: build_gitlab  ## run test for gitlab - do not erase previous database -	cd $(project); $(PYTHON) manage.py test -k --exclude-tag no_ci --exclude-tag gis --exclude-tag ui --exclude-tag libreoffice $(apps) +	cd $(project); $(PYTHON) manage.py test --keep --exclude-tag no_ci --exclude-tag gis --exclude-tag ui --exclude-tag libreoffice $(apps)  run_libreoffice:  ## run libreoffice daemon for testing purpose  	/usr/bin/libreoffice --headless --accept="socket,host=127.0.0.1,port=8101;urp;" --nodefault --nofirststartwizard --nolockcheck --nologo --norestore --invisible --pidfile=/var/run/libreoffice.pid diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 33af742ac..01fb4f554 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -99,7 +99,7 @@ class Job(GeneralType):      )      ground_daily_cost = models.FloatField(_("Ground daily cost"), blank=True, null=True)      daily_cost = models.FloatField(_("Daily cost"), blank=True, null=True) -    permanent_contract = models.NullBooleanField( +    permanent_contract = models.BooleanField(          _("Permanent contract"), blank=True, null=True      )      default_daily_need_on_ground = models.FloatField( @@ -790,16 +790,16 @@ class File(      research_comment = models.TextField(          _("Research archaeology comment"), blank=True, default=""      ) -    classified_area = models.NullBooleanField( +    classified_area = models.BooleanField(          _("Classified area"), blank=True, null=True      ) -    protected_area = models.NullBooleanField(_("Protected area"), blank=True, null=True) +    protected_area = models.BooleanField(_("Protected area"), blank=True, null=True)      if settings.COUNTRY == "fr": -        cira_advised = models.NullBooleanField("Passage en CIRA", blank=True, null=True) -        mh_register = models.NullBooleanField( +        cira_advised = models.BooleanField("Passage en CIRA", blank=True, null=True) +        mh_register = models.BooleanField(              "Sur Monument Historique classé", blank=True, null=True          ) -        mh_listing = models.NullBooleanField( +        mh_listing = models.BooleanField(              "Sur Monument Historique inscrit", blank=True, null=True          )      # <-- research archaeology diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index 6104587c2..7f345efd8 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -2237,7 +2237,7 @@ class Find(          _("Reference container - first full location"), default="", blank=True,          help_text=_("Updated as long as no packaging is attached")      ) -    is_complete = models.NullBooleanField(_("Is complete?"), blank=True, null=True) +    is_complete = models.BooleanField(_("Is complete?"), blank=True, null=True)      object_types = models.ManyToManyField(          ObjectType, verbose_name=_("Object types"), related_name="find", blank=True      ) diff --git a/archaeological_operations/models.py b/archaeological_operations/models.py index 614ee69aa..849376958 100644 --- a/archaeological_operations/models.py +++ b/archaeological_operations/models.py @@ -1642,14 +1642,14 @@ class Operation(      # preventive      fnap_cost = models.IntegerField("Financement FNAP (€)", blank=True, null=True)      # preventive diag -    zoning_prescription = models.NullBooleanField( +    zoning_prescription = models.BooleanField(          _("Prescription on zoning"), blank=True, null=True      )      # preventive diag -    large_area_prescription = models.NullBooleanField( +    large_area_prescription = models.BooleanField(          _("Prescription on large area"), blank=True, null=True      ) -    geoarchaeological_context_prescription = models.NullBooleanField( +    geoarchaeological_context_prescription = models.BooleanField(          _("Prescription on geoarchaeological context"), blank=True, null=True      )  # preventive diag      cira_rapporteur = models.ForeignKey( @@ -1660,7 +1660,7 @@ class Operation(          on_delete=models.SET_NULL,          verbose_name="Rapporteur CTRA/CIRA",      ) -    negative_result = models.NullBooleanField( +    negative_result = models.BooleanField(          _("Result considered negative"), blank=True, null=True      )      cira_date = models.DateField("Date avis CTRA/CIRA", null=True, blank=True) @@ -1720,14 +1720,14 @@ class Operation(      documentation_deadline = models.DateField(          _("Deadline for submission of the documentation"), blank=True, null=True      ) -    documentation_received = models.NullBooleanField( +    documentation_received = models.BooleanField(          _("Documentation provided"), blank=True, null=True      )      finds_deposit_date = models.DateField(_("Finds deposit date"), blank=True, null=True)      finds_deadline = models.DateField(          _("Deadline for submission of the finds"), blank=True, null=True      ) -    finds_received = models.NullBooleanField(_("Finds provided"), blank=True, null=True) +    finds_received = models.BooleanField(_("Finds provided"), blank=True, null=True)      # underwater      drassm_code = models.CharField( diff --git a/docs/generate_values_doc.py b/docs/generate_values_doc.py index 6d9ca6d42..e71278ded 100644 --- a/docs/generate_values_doc.py +++ b/docs/generate_values_doc.py @@ -23,7 +23,6 @@ TYPES = {  # put more precise fields after generic field. e.g. IntegerField befo      'DateTimeField': "Date/heure",      'DateField': "Date",      'EmailField': "Courriel", -    'NullBooleanField': "Booléen",      'BooleanField': "Booléen",      'ImageField': "Image",      'FileField': "Fichier", diff --git a/example_project/settings.py b/example_project/settings.py index 2cf0015d7..e8539e71f 100644 --- a/example_project/settings.py +++ b/example_project/settings.py @@ -20,6 +20,7 @@ DEBUG_TO_CONSOLE = False  SQL_DEBUG = False  DJANGO_EXTENSIONS = False  TEST_VIEWS = True +DEFAULT_AUTO_FIELD = "django.db.models.AutoField"  if "test" in sys.argv:      sys.path.insert(0, "..") diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index 5775eada8..dbce4670f 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -2539,11 +2539,16 @@ class JsonContentTypeFormMixin(object):          exclude = []      def __init__(self, *args, **kwargs): -        super(JsonContentTypeFormMixin, self).__init__(*args, **kwargs) +        super().__init__(*args, **kwargs)          choices = [] -        for pk, label in self.fields["content_type"].choices: -            if not pk: -                choices.append((pk, label)) +        for choice, label in self.fields["content_type"].choices: +            if not choice: +                choices.append(('', label)) +                continue +            try: +                pk = int(choice.value) +            except ValueError: +                choices.append(('', label))                  continue              ct = ContentType.objects.get(pk=pk)              model_class = ct.model_class() diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index d9d7a4bac..9dbe0b3e9 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -31,8 +31,7 @@ from django.conf import settings  from django.contrib.auth.models import User  from django.contrib.contenttypes.models import ContentType  from django.contrib.gis.geos.error import GEOSException -from django.db.models.fields import FieldDoesNotExist -from django.core.exceptions import FieldError, MultipleObjectsReturned +from django.core.exceptions import FieldDoesNotExist, FieldError, MultipleObjectsReturned  from django.core.files import File  from django.db import IntegrityError, DatabaseError, transaction  from django.db.models import Q diff --git a/ishtar_common/jinja_filters.py b/ishtar_common/jinja_filters.py index cf19ef6c7..94be3a246 100644 --- a/ishtar_common/jinja_filters.py +++ b/ishtar_common/jinja_filters.py @@ -19,7 +19,7 @@  from datetime import datetime  from jinja2.environment import Environment -from jinja2.filters import environmentfilter +from jinja2.filters import pass_environment as environmentfilter  import locale  from num2words import num2words  import re diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 95dd5cac2..da827f99b 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -60,7 +60,7 @@ from django.contrib.gis.db import models  from django.contrib.gis.db.models.aggregates import Union  from django.contrib.gis.geos.polygon import Polygon  from django.contrib.gis.geos.collections import MultiPolygon -from django.contrib.postgres.fields import JSONField, ArrayField +from django.contrib.postgres.fields import ArrayField  from django.contrib.postgres.indexes import GinIndex  from django.contrib.sites.models import Site  from django.core.cache import cache @@ -72,7 +72,7 @@ from django.core.exceptions import (  from django.core.files.base import ContentFile  from django.core.files.uploadedfile import SimpleUploadedFile  from django.db import connection, transaction -from django.db.models import Q, Max, Count +from django.db.models import JSONField, Q, Max, Count  from django.db.models.signals import post_save, post_delete, pre_delete, m2m_changed  from django.db.utils import DatabaseError  from django.template import Context, Template @@ -3012,7 +3012,7 @@ class Person(Address, Merge, OwnPerms, ValueGetter, MainItem):          "ishtaruser__isnull": "ishtaruser__isnull",          "attached_to": "attached_to",      } -    COL_LABELS = {"attached_to": _("Organization")} +    COL_LABELS = {"attached_to": _("Organization"), "person_types_list": _("Types")}      # alternative names of fields for searches      ALT_NAMES = { @@ -5268,7 +5268,7 @@ class Document(      additional_information = models.TextField(          _("Additional information"), blank=True, default=""      ) -    duplicate = models.NullBooleanField(_("Has a duplicate"), blank=True, null=True) +    duplicate = models.BooleanField(_("Has a duplicate"), blank=True, null=True)      associated_links = models.TextField(_("Symbolic links"), blank=True, default="")      cache_related_label = models.TextField(          _("Related"), diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index ce31cb974..a5b4338f2 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -30,7 +30,6 @@ from django.contrib.contenttypes.fields import GenericForeignKey  from django.contrib.gis.db import models  from django.contrib.gis.geos import GEOSGeometry, Point  from django.contrib.gis.gdal.error import GDALException -from django.contrib.postgres.fields import JSONField  from django.contrib.postgres.search import SearchVectorField, SearchVector  from django.contrib.sites.models import Site  from django.core.cache import cache as django_cache @@ -40,7 +39,7 @@ from django.core.serializers import serialize  from django.urls import reverse, NoReverseMatch  from django.core.validators import validate_slug  from django.db import connection, transaction, OperationalError, IntegrityError -from django.db.models import Q, Count, Max +from django.db.models import JSONField, Q, Count, Max  from django.db.models.signals import post_save, post_delete, m2m_changed  from django.template import loader  from django.template.defaultfilters import slugify @@ -4399,7 +4398,7 @@ class Merge(models.Model):      merge_key = models.TextField(_("Merge key"), blank=True, null=True)      merge_candidate = models.ManyToManyField("self", blank=True)      merge_exclusion = models.ManyToManyField("self", blank=True) -    archived = models.NullBooleanField(default=False, blank=True, null=True) +    archived = models.BooleanField(default=False, blank=True, null=True)      # 1 for one word similarity, 2 for two word similarity, etc.      MERGE_CLEMENCY = None      EMPTY_MERGE_KEY = "--" diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py index a8866568b..a935c0e21 100644 --- a/ishtar_common/models_imports.py +++ b/ishtar_common/models_imports.py @@ -1358,6 +1358,7 @@ def convert_geom(feature, srid):      if not feature:          return feature      srid = int(srid) +    feature = dict(feature)      geo_type = feature["type"]      if geo_type in ("LineString", "Polygon"):          feature["type"] = "Multi" + geo_type @@ -2269,7 +2270,8 @@ class Import(BaseImport):          if self.number_of_line:              return self.number_of_line          imported_values = self.get_imported_values() -        if (not imported_values or not imported_values.path or not imported_values.path.endswith(".csv")) \ +        if (not imported_values or not imported_values.path +                or not imported_values.path.endswith(".csv")) \                  and self.importer_type.type == "gis":              return self._data_table_gis(get_number_only=True)          if not imported_values or not imported_values.path: @@ -2278,8 +2280,11 @@ class Import(BaseImport):          encodings = [self.encoding]          encodings += [coding for coding, c in ENCODINGS if coding != self.encoding]          for encoding in encodings: +            options = {} +            if encoding: +                options["encoding"] = encoding              try: -                with open(filename, "r", encoding=encoding) as f: +                with open(filename, "r", **options) as f:                      reader = csv.reader(f, delimiter=self.csv_sep)                      nb = sum(1 for __ in reader) - self.skip_lines              except UnicodeDecodeError: @@ -2517,6 +2522,8 @@ class Import(BaseImport):                  # https://pyproj4.github.io/pyproj/stable/gotchas.html#axis-order-changes-in-proj-6                  elif crs.startswith("+init=epsg:"):                      srid = crs[len("+init=epsg:"):] +                elif crs.lower().startswith("epsg:"): +                    srid = crs[len("epsg:"):]                  else:                      srid = CRS.from_proj4(crs).to_epsg()                  data = [] diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 768c58495..65c0128ab 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -30,7 +30,7 @@ import hashlib  from importlib import import_module  import io  from jinja2 import Template -from jinja2.filters import FILTERS, environmentfilter +from jinja2.filters import FILTERS, pass_environment  import locale  import math  import numpy @@ -2729,7 +2729,7 @@ PARSE_JINJA = re.compile("{{([^}]*)}")  PARSE_JINJA_IF = re.compile("{% if ([^}]*)}") -@environmentfilter +@pass_environment  def _deduplicate(*args):      value = args[0] if len(args) == 1 else args[1]  # jinja simple filter      new_values = [] @@ -2748,55 +2748,55 @@ def _padding(formt, args):      return formt.format(value) -@environmentfilter +@pass_environment  def _padding03(*args):      return _padding("{:0>3}", args) -@environmentfilter +@pass_environment  def _padding04(*args):      return _padding("{:0>4}", args) -@environmentfilter +@pass_environment  def _padding05(*args):      return _padding("{:0>5}", args) -@environmentfilter +@pass_environment  def _padding06(*args):      return _padding( "{:0>6}", args) -@environmentfilter +@pass_environment  def _padding07(*args):      return _padding( "{:0>7}", args) -@environmentfilter +@pass_environment  def _padding08(*args):      return _padding( "{:0>8}", args) -@environmentfilter +@pass_environment  def _upper(*args):      value = args[0] if len(args) == 1 else args[1]  # jinja simple filter      return value.upper() -@environmentfilter +@pass_environment  def _lower(*args):      value = args[0] if len(args) == 1 else args[1]  # jinja simple filter      return value.lower() -@environmentfilter +@pass_environment  def _capitalize(*args):      value = args[0] if len(args) == 1 else args[1]  # jinja simple filter      return value.capitalize() -@environmentfilter +@pass_environment  def _slug(*args):      value = args[0] if len(args) == 1 else args[1]  # jinja simple filter      return slugify(value) diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 53a42d6ae..6c7b85130 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -261,7 +261,7 @@ def new_qa_item(      model, frm, many=False, template="ishtar/forms/qa_new_item.html", page_name="",      callback=None  ): -    def func(request, parent_name, limits=""): +    def func(request, parent_name=None, limits=""):          not_permitted_msg = ugettext("Operation not permitted.")          meta = model._meta          permission = f"{meta.app_label}.add_{meta.model_name}" @@ -899,7 +899,7 @@ def _parse_parentheses_groups(          exc_dct = {}      if not extra_distinct_q:          extra_distinct_q = [] -    if type(groups) is not list: +    if not isinstance(groups, list):          string = groups.strip()          if string.startswith('"') and string.endswith('"') and string.count('"') == 2:              string = string[1:-1] @@ -2746,8 +2746,10 @@ def get_item(                  and_reqs[:]              ) -            # print("ishtar_common/views_item.py - 2515") -            # print(query, distinct_queries, base_query, exc_query, extras) +            # print("ishtar_common/views_item.py - 2745") +            # print(f"query: {query}", f"distinct_queries: {distinct_queries}", +            #       f"base_query: {base_query}", f"exc_query: {exc_query}", +            #       f"extras: {extras}")              sub_items = model.objects.filter(query)              for d_q in distinct_queries:                  sub_items = sub_items.filter(d_q) diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index be6cb23e8..97ed7deec 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -22,10 +22,8 @@ import logging  from django import forms  from django.conf import settings  from django.contrib.gis import forms as gis_forms -from django.contrib.gis.db import models as gis_models -from django.core.exceptions import ValidationError +from django.core.exceptions import FieldDoesNotExist, ValidationError  from django.core.files import File -from django.db.models import fields  from django.forms import ClearableFileInput  from django.forms.utils import flatatt  from django.forms.widgets import ( @@ -1261,7 +1259,7 @@ class DataTable(Select2Media, forms.RadioSelect):                      try:                          field = field._meta.get_field(key)                          field_verbose_name = field.verbose_name -                    except (fields.FieldDoesNotExist, AttributeError): +                    except (FieldDoesNotExist, AttributeError, KeyError):                          if hasattr(field, key + "_lbl"):                              field_verbose_name = getattr(field, key + "_lbl")                          else: | 
