wip/74843-compatibilite-django #1
6
setup.py
6
setup.py
|
@ -106,13 +106,11 @@ setup(
|
|||
'Programming Language :: Python',
|
||||
],
|
||||
install_requires=[
|
||||
'django<2.3',
|
||||
'django<3.3',
|
||||
'isodate',
|
||||
'psycopg2<2.9',
|
||||
'jsonschema',
|
||||
'gadjo',
|
||||
'six',
|
||||
'djangorestframework>=3.3,<3.10',
|
||||
'djangorestframework>=3.9,<3.13',
|
||||
'pytz',
|
||||
'python-dateutil',
|
||||
'django-admin-rangefilter',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import io
|
||||
import mock
|
||||
import pytest
|
||||
import sys
|
||||
|
@ -9,12 +10,11 @@ from zoo.zoo_nanterre import utils
|
|||
|
||||
from django.core.management import call_command
|
||||
from django.db.transaction import atomic
|
||||
from django.utils.six import StringIO
|
||||
|
||||
|
||||
def get_output_of_command(command, *args, **kwargs):
|
||||
old_stdout = sys.stdout
|
||||
output = sys.stdout = StringIO()
|
||||
output = sys.stdout = io.StringIO()
|
||||
call_command(command, *args, **kwargs)
|
||||
sys.stdout = old_stdout
|
||||
return output.getvalue()
|
||||
|
|
|
@ -5,14 +5,13 @@ import datetime
|
|||
import isodate
|
||||
import requests
|
||||
import threading
|
||||
import urllib.parse
|
||||
|
||||
import pytest
|
||||
import httmock
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.six.moves.urllib import parse as urlparse
|
||||
from django.utils.timezone import now
|
||||
|
||||
from zoo.zoo_data.models import Entity, Relation, Log, Job
|
||||
|
@ -693,9 +692,9 @@ def test_create_individu(settings, transactional_db, app, app_noauth, rsu_schema
|
|||
more = response.json.get('more')
|
||||
if more:
|
||||
assert 'cookie' in response.json
|
||||
parsed = urlparse.urlparse(response.json['more'])
|
||||
parsed = urllib.parse.urlparse(response.json['more'])
|
||||
query = parsed.query
|
||||
assert urlparse.parse_qs(query)['cookie'] == [response.json['cookie']]
|
||||
assert urllib.parse.parse_qs(query)['cookie'] == [response.json['cookie']]
|
||||
assert sorted(d['id'] for d in all_data) == sorted(qs.values_list('id', flat=True))
|
||||
assert count == qs.count()
|
||||
assert 'more' not in response.json
|
||||
|
@ -1294,7 +1293,7 @@ def test_passage_a_la_majorite(db, settings, nanterre_classic_family, freezer):
|
|||
Job.redo(timestamp=now() + datetime.timedelta(seconds=20))
|
||||
assert len(requests) == 1
|
||||
|
||||
req_content = json.loads(force_text(requests[0].body))
|
||||
req_content = json.loads(requests[0].body)
|
||||
assert req_content['metadonnees']['service'] == 'passage-majorite'
|
||||
assert len(req_content['fragments']) == 3
|
||||
assert req_content['fragments'][0]['type'] == 'maj-adresse'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import copy
|
||||
import urllib
|
||||
|
||||
from django.urls import reverse
|
||||
from django.core.management import call_command
|
||||
from django.utils.six.moves.urllib import parse as urlparse
|
||||
|
||||
from zoo.zoo_nanterre.models import Duplicate
|
||||
from zoo.zoo_data.models import Log, Entity
|
||||
|
@ -43,8 +43,8 @@ def test_list_doublons(nanterre_classic_family, app):
|
|||
assert response.json['err'] == 0
|
||||
assert 'more' in response.json
|
||||
assert 'cookie' in response.json
|
||||
assert response.json['cookie'] == urlparse.parse_qs(
|
||||
urlparse.urlparse(
|
||||
assert response.json['cookie'] == urllib.parse.parse_qs(
|
||||
urllib.parse.urlparse(
|
||||
response.json['more']).query)['cookie'][0]
|
||||
assert len(response.json['data']) >= 10
|
||||
assert response.json['data'][0]['id'] == d.id
|
||||
|
|
|
@ -4,7 +4,6 @@ import json
|
|||
import httmock
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from zoo.zoo_nanterre.fragments import Synchronization
|
||||
from zoo.models import Job
|
||||
|
@ -29,7 +28,7 @@ def test_synchro_full(app, nanterre_classic_family):
|
|||
|
||||
@httmock.urlmatch()
|
||||
def technocarte_ok(url, request):
|
||||
request_bodies.append(json.loads(force_text(request.body)))
|
||||
request_bodies.append(json.loads(request.body))
|
||||
return httmock.response(
|
||||
200, [
|
||||
{
|
||||
|
@ -228,7 +227,7 @@ def test_infor(app, nanterre_classic_family):
|
|||
|
||||
@httmock.urlmatch()
|
||||
def infor_ok(url, request):
|
||||
request_bodies.append(json.loads(force_text(request.body)))
|
||||
request_bodies.append(json.loads(request.body))
|
||||
return httmock.response(
|
||||
200, {
|
||||
'http_code': 200,
|
||||
|
@ -277,7 +276,7 @@ def test_infor(app, nanterre_classic_family):
|
|||
|
||||
@httmock.urlmatch()
|
||||
def infor_nok(url, request):
|
||||
request_bodies.append(json.loads(force_text(request.body)))
|
||||
request_bodies.append(json.loads(request.body))
|
||||
return httmock.response(
|
||||
200, {
|
||||
'http_code': 500,
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import csv
|
||||
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.six import StringIO
|
||||
import io
|
||||
|
||||
from webtest import Upload
|
||||
|
||||
|
@ -26,19 +23,15 @@ def test_synchronize_federations(settings, app, nanterre_classic_family, admin):
|
|||
response = response.click(u'Synchroniser les fédérations')
|
||||
response = response.click(u'Nouvel import')
|
||||
response.form.set('app_id', 'technocarte')
|
||||
content = force_bytes('\n'.join(map(str, [f['kevin'].id + 1000, f['marie'].id + 1000, '99999'])))
|
||||
content = ('\n'.join(map(str, [f['kevin'].id + 1000, f['marie'].id + 1000, '99999']))).encode()
|
||||
response.form.set('csv_uploaded', Upload('federations.csv', content, 'application/octet-stream'))
|
||||
response = response.form.submit().follow()
|
||||
assert len(response.pyquery('table#result-list tbody tr')) == 1
|
||||
response = response.click('Rapport')
|
||||
|
||||
def check_csv_response(csv_response):
|
||||
if six.PY3:
|
||||
reader = csv.DictReader(StringIO(csv_response.text))
|
||||
reader.fieldnames = reader.reader.__next__()
|
||||
else:
|
||||
reader = csv.DictReader(StringIO(csv_response.content))
|
||||
reader.fieldnames = reader.reader.next()
|
||||
reader = csv.DictReader(io.StringIO(csv_response.text))
|
||||
reader.fieldnames = reader.reader.__next__()
|
||||
rows = list(reader)
|
||||
|
||||
def rows_by_action(action):
|
||||
|
|
5
tox.ini
5
tox.ini
|
@ -18,11 +18,12 @@ setenv =
|
|||
usedevelop = true
|
||||
deps =
|
||||
dj22: django<2.3
|
||||
dj22: psycopg2-binary<2.9
|
||||
dj22: djangorestframework>=3.9.2,<3.10
|
||||
dj32: django>=3.2,<3.3
|
||||
djangorestframework>=3.9.2,<3.10
|
||||
dj32: psycopg2-binary
|
||||
pip>8
|
||||
pytest-flakes
|
||||
pg: psycopg2<2.9
|
||||
coverage
|
||||
pytest-cov
|
||||
pytest-django
|
||||
|
|
27
zoo/urls.py
27
zoo/urls.py
|
@ -14,30 +14,15 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""zoo URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/1.10/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.conf.urls import url, include
|
||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls import url, include
|
||||
from django.urls import re_path, include
|
||||
from django.contrib import admin
|
||||
|
||||
from .views import login, logout
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^demo/', include('zoo.zoo_demo.urls')),
|
||||
url(r'^rsu/', include('zoo.zoo_nanterre.urls')),
|
||||
url(r'^logout/$', logout, name='logout'),
|
||||
url(r'^login/$', login, name='auth_login'),
|
||||
re_path(r'^admin/', admin.site.urls),
|
||||
re_path(r'^demo/', include('zoo.zoo_demo.urls')),
|
||||
re_path(r'^rsu/', include('zoo.zoo_nanterre.urls')),
|
||||
re_path(r'^logout/$', logout, name='logout'),
|
||||
re_path(r'^login/$', login, name='auth_login'),
|
||||
]
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
import unicodedata
|
||||
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from rest_framework.views import exception_handler
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
@ -33,8 +31,8 @@ def rest_exception_handler(exc, context):
|
|||
raise
|
||||
response = Response({
|
||||
'err': 1,
|
||||
'exc_class': force_text(exc.__class__),
|
||||
'exc_value': force_text(exc),
|
||||
'exc_class': str(exc.__class__),
|
||||
'exc_value': str(exc),
|
||||
})
|
||||
response.status = 400
|
||||
return response
|
||||
|
|
|
@ -14,4 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
default_app_config = 'zoo.zoo_data.apps.ZooDataConfig'
|
||||
import django
|
||||
|
||||
if django.VERSION < (3, 2):
|
||||
default_app_config = 'zoo.zoo_data.apps.ZooDataConfig'
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class ZooDataConfig(AppConfig):
|
||||
|
|
|
@ -16,87 +16,40 @@
|
|||
|
||||
import django
|
||||
|
||||
from django.db.models import Transform, TextField, DateField
|
||||
from django.db.models import Transform, CharField, DateField
|
||||
from django.db.models.functions import Lower
|
||||
|
||||
from django.contrib.postgres.fields import jsonb
|
||||
try:
|
||||
from django.contrib.postgres.fields.jsonb import KeyTransform, KeyTransformTextLookupMixin
|
||||
from django.db.models.fields.json import KeyTransform, KeyTransformTextLookupMixin
|
||||
except ImportError:
|
||||
# backport from Django 2.x
|
||||
class KeyTransform(Transform):
|
||||
operator = '->'
|
||||
nested_operator = '#>'
|
||||
|
||||
def __init__(self, key_name, *args, **kwargs):
|
||||
super(KeyTransform, self).__init__(*args, **kwargs)
|
||||
self.key_name = key_name
|
||||
|
||||
def as_sql(self, compiler, connection):
|
||||
key_transforms = [self.key_name]
|
||||
previous = self.lhs
|
||||
while isinstance(previous, KeyTransform):
|
||||
key_transforms.insert(0, previous.key_name)
|
||||
previous = previous.lhs
|
||||
lhs, params = compiler.compile(previous)
|
||||
if len(key_transforms) > 1:
|
||||
return "(%s %s %%s)" % (lhs, self.nested_operator), [key_transforms] + params
|
||||
try:
|
||||
int(self.key_name)
|
||||
except ValueError:
|
||||
lookup = "'%s'" % self.key_name
|
||||
else:
|
||||
lookup = "%s" % self.key_name
|
||||
return "(%s %s %s)" % (lhs, self.operator, lookup), params
|
||||
|
||||
jsonb.KeyTransform = KeyTransform
|
||||
|
||||
class KeyTextTransform(KeyTransform):
|
||||
operator = '->>'
|
||||
nested_operator = '#>>'
|
||||
_output_field = TextField()
|
||||
|
||||
class KeyTransformTextLookupMixin(object):
|
||||
"""
|
||||
Mixin for combining with a lookup expecting a text lhs from a JSONField
|
||||
key lookup. Make use of the ->> operator instead of casting key values to
|
||||
text and performing the lookup on the resulting representation.
|
||||
"""
|
||||
def __init__(self, key_transform, *args, **kwargs):
|
||||
assert isinstance(key_transform, KeyTransform)
|
||||
key_text_transform = KeyTextTransform(
|
||||
key_transform.key_name, *key_transform.source_expressions, **key_transform.extra
|
||||
)
|
||||
super(KeyTransformTextLookupMixin, self).__init__(key_text_transform, *args, **kwargs)
|
||||
|
||||
|
||||
class Lower(Transform):
|
||||
lookup_name = 'lower'
|
||||
function = 'LOWER'
|
||||
|
||||
TextField.register_lookup(Lower)
|
||||
from django.contrib.postgres.fields.jsonb import KeyTransform, KeyTransformTextLookupMixin
|
||||
|
||||
|
||||
class Unaccent(Transform):
|
||||
lookup_name = 'unaccent'
|
||||
function = 'immutable_unaccent'
|
||||
output_field = CharField()
|
||||
|
||||
TextField.register_lookup(Unaccent)
|
||||
CharField.register_lookup(Unaccent)
|
||||
|
||||
CharField.register_lookup(Lower)
|
||||
|
||||
class Normalize(Transform):
|
||||
lookup_name = 'normalize'
|
||||
function = 'immutable_normalize'
|
||||
output_field = CharField()
|
||||
|
||||
TextField.register_lookup(Normalize)
|
||||
CharField.register_lookup(Normalize)
|
||||
|
||||
|
||||
class Date(Transform):
|
||||
lookup_name = 'timestamp'
|
||||
function = 'immutable_date'
|
||||
_output_field = DateField()
|
||||
output_field = DateField()
|
||||
|
||||
|
||||
TextField.register_lookup(Date)
|
||||
CharField.register_lookup(Date)
|
||||
|
||||
|
||||
class JSONUnaccent(KeyTransformTextLookupMixin, Unaccent):
|
||||
|
|
|
@ -24,22 +24,20 @@ import datetime
|
|||
|
||||
from django.db import models, connection
|
||||
from django.db.models import F, Value
|
||||
from django.db.models.functions import Lower
|
||||
from django.db.models.query import QuerySet, Q
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.six import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.timezone import now
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from django.contrib.postgres.search import TrigramDistance
|
||||
|
||||
|
||||
from .search import Unaccent, Lower, JSONTextRef
|
||||
from .search import Unaccent, JSONTextRef
|
||||
from zoo.zoo_meta.validators import schema_validator
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Transaction(models.Model):
|
||||
created = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
|
@ -54,7 +52,7 @@ class Transaction(models.Model):
|
|||
null=True)
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.id)
|
||||
return str(self.id)
|
||||
|
||||
@classmethod
|
||||
def get_transaction(self):
|
||||
|
@ -77,7 +75,7 @@ class EntityQuerySet(QuerySet):
|
|||
for key, value in kwargs.items():
|
||||
filters.append(Q(**{
|
||||
'content__' + key + '__unaccent__lower__trigram_similar':
|
||||
Lower(Unaccent(Value(value))),
|
||||
Unaccent(Lower(Value(value))),
|
||||
}))
|
||||
qs = qs.filter(functools.reduce(__or__, filters))
|
||||
expressions = []
|
||||
|
@ -93,7 +91,6 @@ class EntityQuerySet(QuerySet):
|
|||
return qs
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CommonData(models.Model):
|
||||
def clean(self):
|
||||
if self.schema:
|
||||
|
@ -103,7 +100,7 @@ class CommonData(models.Model):
|
|||
raise ValidationError({'content': e})
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.id)
|
||||
return str(self.id)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -255,7 +252,6 @@ class JobQuerySet(QuerySet):
|
|||
return self.filter(**{'content__$classpath': class_path})
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Job(models.Model):
|
||||
'''Store synchronization messages sent to applications'''
|
||||
SCHEDULER_STEP = 60 * 5 # 5 minutes
|
||||
|
@ -334,7 +330,7 @@ class Job(models.Model):
|
|||
job.state = cls.STATE_UNRECOVERABLE_ERROR
|
||||
error = job.content.setdefault('error', {})
|
||||
error['code'] = 'internal-server-error'
|
||||
error['exc_detail'] = force_text(e)
|
||||
error['exc_detail'] = str(e)
|
||||
error['exc_tb'] = traceback.format_exc()
|
||||
job.get_logger().exception('exception during job %s', job.admin_url)
|
||||
job.save()
|
||||
|
@ -366,7 +362,7 @@ class Job(models.Model):
|
|||
url = self.admin_url
|
||||
self.get_logger().exception('exception during job %s', url)
|
||||
self.state = self.STATE_UNRECOVERABLE_ERROR
|
||||
self.content['$exc_detail'] = force_text(e)
|
||||
self.content['$exc_detail'] = str(e)
|
||||
self.content['$exc_tb'] = traceback.format_exc()
|
||||
self.content['$classpath'] = self.get_classpath(action)
|
||||
self.save()
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.db.models import Func, Value
|
||||
from django.db.models import Func, Value, CharField
|
||||
|
||||
|
||||
class Unaccent(Func):
|
||||
|
@ -27,11 +27,6 @@ class Normalize(Func):
|
|||
arity = 1
|
||||
|
||||
|
||||
class Lower(Func):
|
||||
function = 'LOWER'
|
||||
arity = 1
|
||||
|
||||
|
||||
class JSONRef(Func):
|
||||
function = ''
|
||||
arg_joiner = '->'
|
||||
|
@ -46,6 +41,7 @@ class JSONTextRef(Func):
|
|||
function = ''
|
||||
arg_joiner = '->>'
|
||||
arity = 2
|
||||
output_field = CharField()
|
||||
|
||||
def __init__(self, *expressions, **extra):
|
||||
jsonb = expressions[0]
|
||||
|
|
|
@ -856,4 +856,4 @@ c&&c.destroy(),
|
|||
c=new f(this.get(0),a),this.data("jsoneditor",c),
|
||||
// Setup event listeners
|
||||
c.on("change",function(){b.trigger("change")}),c.on("ready",function(){b.trigger("ready")}))}return this}}}(),window.JSONEditor=f}();
|
||||
//# sourceMappingURL=jsoneditor.min.js.map
|
||||
//# sourceMappingURL=jsoneditor.min.js.map
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import schemas, schema
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^schemas/$', schemas, name='demo-schemas'),
|
||||
url(r'^schemas/(?P<name>\w*)/$', schema, name='demo-schema'),
|
||||
re_path(r'^schemas/$', schemas, name='demo-schemas'),
|
||||
re_path(r'^schemas/(?P<name>\w*)/$', schema, name='demo-schema'),
|
||||
]
|
||||
|
|
|
@ -14,4 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
default_app_config = 'zoo.zoo_meta.apps.ZooMetaAppConfig'
|
||||
import django
|
||||
|
||||
if django.VERSION < (3, 2):
|
||||
default_app_config = 'zoo.zoo_meta.apps.ZooMetaAppConfig'
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db.models.signals import post_migrate, post_save
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
from __future__ import print_function
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from zoo.zoo_meta.models import EntitySchema
|
||||
|
||||
|
@ -25,7 +24,7 @@ class Command(BaseCommand):
|
|||
def handle(self, *args, **options):
|
||||
for schema in EntitySchema.objects.all():
|
||||
if options['verbosity'] >= 1:
|
||||
print('Rebuilding index for', force_text(schema), end=' ')
|
||||
print('Rebuilding index for', schema, end=' ')
|
||||
schema.rebuild_indexes()
|
||||
if options['verbosity'] >= 1:
|
||||
print(' Done.')
|
||||
|
|
|
@ -18,8 +18,7 @@ from hashlib import md5
|
|||
|
||||
from django.apps import apps
|
||||
from django.db import models, connection
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
|
||||
|
||||
|
@ -74,7 +73,7 @@ class CommonSchema(models.Model):
|
|||
|
||||
def rebuild_string_index(self, cursor, table, path):
|
||||
expr = 'immutable_normalize((content%s))' % self.path_to_sql_expr(path)
|
||||
key = md5(force_bytes(expr)).hexdigest()[:8]
|
||||
key = md5(expr.encode()).hexdigest()[:8]
|
||||
sql = ('CREATE INDEX zoo_entity_%s_gin_%s_dynamic_idx ON %s USING gin ((%s) '
|
||||
' gin_trgm_ops) WHERE schema_id = %s' % (key, self.id, table, expr, self.id))
|
||||
cursor.execute(sql)
|
||||
|
@ -84,7 +83,7 @@ class CommonSchema(models.Model):
|
|||
|
||||
def rebuild_string_date_time_index(self, cursor, table, path):
|
||||
expr = 'immutable_date(content%s)' % self.path_to_sql_expr(path)
|
||||
key = md5(force_bytes(expr)).hexdigest()[:8]
|
||||
key = md5(expr.encode()).hexdigest()[:8]
|
||||
sql = ('CREATE INDEX zoo_entity_%s_%s_dynamic_idx ON %s (%s) '
|
||||
'WHERE schema_id = %s' % (key, self.id, table, expr, self.id))
|
||||
cursor.execute(sql)
|
||||
|
@ -99,7 +98,7 @@ class CommonSchema(models.Model):
|
|||
else:
|
||||
raise NotImplementedError(self)
|
||||
|
||||
key = md5(force_bytes(expr)).hexdigest()[:8]
|
||||
key = md5(expr.encode()).hexdigest()[:8]
|
||||
gin_sql = ('CREATE INDEX zoo_entity_%s_gin_%s_dynamic_idx ON %s USING gin ((%s) '
|
||||
'gin_trgm_ops) WHERE schema_id = %s' % (key, self.id, table, expr, self.id))
|
||||
gist_sql = ('CREATE INDEX zoo_entity_%s_gist_%s_dynamic_idx ON %s USING gist ((%s)'
|
||||
|
@ -147,8 +146,8 @@ class CommonSchema(models.Model):
|
|||
try:
|
||||
return eval(self.caption_template, {}, value.content)
|
||||
except Exception as e:
|
||||
return force_text(e)
|
||||
return force_text(value.id)
|
||||
return str(e)
|
||||
return str(value.id)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
|
@ -14,4 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
default_app_config = 'zoo.zoo_nanterre.apps.ZooNanterreConfig'
|
||||
import django
|
||||
|
||||
if django.VERSION < (3, 2):
|
||||
default_app_config = 'zoo.zoo_nanterre.apps.ZooNanterreConfig'
|
||||
|
|
|
@ -32,7 +32,6 @@ from django.db.models.query import Q
|
|||
from django.db.transaction import non_atomic_requests, atomic
|
||||
from django.urls import reverse
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.timezone import now
|
||||
from django.utils.http import urlencode
|
||||
|
||||
|
@ -135,7 +134,7 @@ class TransactionalView(APIView):
|
|||
content = {
|
||||
'request': self.request.data,
|
||||
'status_code': 500,
|
||||
'$exc_detail': force_text(exc),
|
||||
'$exc_detail': str(exc),
|
||||
'$exc_tb': traceback.format_exc(),
|
||||
}
|
||||
self.transaction.content = content
|
||||
|
@ -2055,7 +2054,7 @@ class FalsePositiveView(DoublonActionView):
|
|||
except AssertionError as e:
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': force_text(e),
|
||||
'errors': str(e),
|
||||
}, status=500)
|
||||
|
||||
|
||||
|
@ -2096,7 +2095,7 @@ class DedupView(DoublonActionView):
|
|||
except AssertionError as e:
|
||||
return Response({
|
||||
'err': 1,
|
||||
'errors': force_text(e),
|
||||
'errors': str(e),
|
||||
}, status=500)
|
||||
|
||||
dedup = DedupView.as_view()
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
import functools
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.conf.urls import url
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.urls import re_path
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
|
||||
|
@ -93,14 +93,14 @@ class ZooNanterreConfig(AppConfig):
|
|||
|
||||
urls = []
|
||||
for desc in descs:
|
||||
urls.append(url(
|
||||
urls.append(re_path(
|
||||
r'^synchronize-federations/%s$' % desc['re'],
|
||||
model_admin.admin_site.admin_view(
|
||||
getattr(views, 'synchronize_federations' + desc['view'])),
|
||||
kwargs={'model_admin': model_admin},
|
||||
name='synchronize-federations' + desc['name'],
|
||||
))
|
||||
urls.append(url(
|
||||
urls.append(re_path(
|
||||
r'^inactive/',
|
||||
model_admin.admin_site.admin_view(
|
||||
getattr(views, 'inactive_index')),
|
||||
|
|
|
@ -15,8 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django import forms
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from .utils import PersonSearch, get_applications, get_application
|
||||
|
@ -63,13 +62,15 @@ class SynchronizeFederationsForm(forms.Form):
|
|||
def clean_csv_uploaded(self):
|
||||
csv_uploaded = self.cleaned_data['csv_uploaded']
|
||||
errors = []
|
||||
csv_uploaded.seek(0)
|
||||
for i, line in enumerate(csv_uploaded):
|
||||
try:
|
||||
force_text(line).encode('ascii')
|
||||
# works with pyhton2 and 3
|
||||
except (UnicodeEncodeError, UnicodeDecodeError) as e:
|
||||
line.decode('ascii')
|
||||
except UnicodeError as e:
|
||||
errors.append(_(u'non-ASCII character on line {0} and column {1}').format(
|
||||
i + 1, e.start + 1))
|
||||
# restore file state
|
||||
csv_uploaded.seek(0)
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
return csv_uploaded
|
||||
|
|
|
@ -5,7 +5,6 @@ import datetime
|
|||
import requests
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
from django.db import DatabaseError
|
||||
|
@ -204,7 +203,7 @@ class FragmentBuilder(object):
|
|||
error_detail = u'erreur réseau/SSL ou expiration'
|
||||
self.error = {
|
||||
'code': 'transport-error',
|
||||
'detail': force_text(e),
|
||||
'detail': str(e),
|
||||
}
|
||||
state = self.state_on_network_error
|
||||
else:
|
||||
|
|
|
@ -23,7 +23,6 @@ import datetime
|
|||
|
||||
import django
|
||||
from django.core.management.base import BaseCommand, CommandParser
|
||||
from django.utils.six import python_2_unicode_compatible
|
||||
from django.utils.timezone import now
|
||||
|
||||
from zoo.zoo_nanterre.utils import individu_caption
|
||||
|
@ -31,7 +30,6 @@ from zoo.zoo_nanterre.duplicates import find_duplicates
|
|||
from zoo.zoo_nanterre.models import Duplicate
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Table(object):
|
||||
def __init__(self, names):
|
||||
self.size = len(names)
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from django.db import models
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ from zoo.models import Job
|
|||
|
||||
import requests
|
||||
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.timezone import now
|
||||
from django.conf import settings
|
||||
|
||||
|
@ -64,7 +63,7 @@ class QF(object):
|
|||
if response:
|
||||
for qf in response:
|
||||
try:
|
||||
qf['annee_imposition'] = force_text(int(re.findall(r'(\d+)', qf['libelle'])[0]) - 1)
|
||||
qf['annee_imposition'] = str(int(re.findall(r'(\d+)', qf['libelle'])[0]) - 1)
|
||||
except Exception:
|
||||
qf['annee_imposition'] = 'inconnue'
|
||||
return response, error
|
||||
|
@ -148,7 +147,7 @@ class QF(object):
|
|||
else:
|
||||
return response[0], None
|
||||
else:
|
||||
return None, u'Implicit calcul-qf réponse invalide: %r' % force_text(response)[:1024]
|
||||
return None, 'Implicit calcul-qf réponse invalide: %r' % str(response)[:1024]
|
||||
|
||||
def lire_quotient_familial(self, individu, date_de_reference):
|
||||
federation = individu.content['cles_de_federation'].get('implicit')
|
||||
|
@ -168,8 +167,8 @@ class QF(object):
|
|||
if isinstance(response, list):
|
||||
return response, None
|
||||
else:
|
||||
return None, (u'Implicit lire-quotient-familial réponse invalide: %r'
|
||||
% force_text(response)[:1024])
|
||||
return None, ('Implicit lire-quotient-familial réponse invalide: %r'
|
||||
% str(response)[:1024])
|
||||
|
||||
def editer_carte(self, individu, id_qf):
|
||||
federation = individu.content['cles_de_federation'].get('implicit')
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import decimal
|
||||
import urllib.parse
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import six, timezone
|
||||
from django.utils.six.moves.urllib import parse as urlparse
|
||||
from django.utils import timezone
|
||||
|
||||
import requests
|
||||
|
||||
|
@ -36,13 +36,13 @@ class Saga(object):
|
|||
|
||||
@property
|
||||
def creance_url(self):
|
||||
return urlparse.urljoin(
|
||||
return urllib.parse.urljoin(
|
||||
self.url,
|
||||
'/%s/services/etat_facture_creance_literal' % self.base_uri)
|
||||
|
||||
@property
|
||||
def paiement_url(self):
|
||||
return urlparse.urljoin(
|
||||
return urllib.parse.urljoin(
|
||||
self.url,
|
||||
'/%s/services/paiement_internet_ws_literal' % self.base_uri)
|
||||
|
||||
|
@ -194,7 +194,7 @@ class Saga(object):
|
|||
<urlretour_synchrone>{urlretour_synchrone}</urlretour_synchrone>
|
||||
</Transaction>'''
|
||||
assert factures, u'factures ne doit pas être vide'
|
||||
id_facture = u'--'.join(six.text_type(facture.num) for facture in factures)
|
||||
id_facture = u'--'.join(str(facture.num) for facture in factures)
|
||||
montant = sum(facture.reste_a_payer for facture in factures)
|
||||
tree, error = self.soap_call(
|
||||
self.paiement_url, body, 'TransactionReturn',
|
||||
|
|
|
@ -17,15 +17,13 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import csv
|
||||
import io
|
||||
|
||||
from django.core.files.storage import default_storage
|
||||
from django.urls import reverse
|
||||
from django.conf import settings
|
||||
from django.db import DatabaseError
|
||||
from django.db.transaction import atomic
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.six import StringIO
|
||||
|
||||
from zoo.zoo_meta.models import EntitySchema
|
||||
from zoo.zoo_data.models import Job, Entity, Transaction, Log
|
||||
|
@ -106,15 +104,12 @@ class SynchronizeFederationsImport(object):
|
|||
self.report('report')
|
||||
|
||||
def report(self, target):
|
||||
output_file = StringIO()
|
||||
output_file = io.StringIO()
|
||||
writer = csv.writer(output_file)
|
||||
writer.writerow(['RSU ID', 'prenoms', 'nom de naissance',
|
||||
'nom d\'usage', 'application', 'federation', 'action'])
|
||||
for action in self.actions:
|
||||
if six.PY3:
|
||||
action = [v for v in action]
|
||||
else:
|
||||
action = [force_bytes(v) for v in action]
|
||||
action = [v for v in action]
|
||||
writer.writerow(action)
|
||||
setattr(self.action, target + '_csv_filename',
|
||||
self.action.csv_filename + '-report.csv')
|
||||
|
|
|
@ -14,74 +14,74 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import demo, search, import_control
|
||||
from . import api_views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^demo/$', demo, name='demo'),
|
||||
url(r'^demo/search/$', search, name='demo'),
|
||||
url(r'^import_control/$', import_control, name='demo'),
|
||||
url(r'^search/$', api_views.search, name='rsu-api-search'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/$', api_views.reseau, name='rsu-api-reseau'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/suppression/$', api_views.suppression_individu,
|
||||
re_path(r'^demo/$', demo, name='demo'),
|
||||
re_path(r'^demo/search/$', search, name='demo'),
|
||||
re_path(r'^import_control/$', import_control, name='demo'),
|
||||
re_path(r'^search/$', api_views.search, name='rsu-api-search'),
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/$', api_views.reseau, name='rsu-api-reseau'),
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/suppression/$', api_views.suppression_individu,
|
||||
name='rsu-api-suppression-individu'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/liste/$', api_views.reseau_liste,
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/liste/$', api_views.reseau_liste,
|
||||
name='rsu-api-reseau-liste'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/journal/$', api_views.journal, name='rsu-api-journal'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/declaration-responsabilite-legale/$',
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/journal/$', api_views.journal, name='rsu-api-journal'),
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/declaration-responsabilite-legale/$',
|
||||
api_views.declaration_responsabilite_legale,
|
||||
name='rsu-api-declaration-responsabilite-legale'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/declaration-adresse-principale/$',
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/declaration-adresse-principale/$',
|
||||
api_views.declaration_adresse_principale,
|
||||
name='rsu-api-declaration-adresse-principale'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/changement-de-situation-maritale/$',
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/changement-de-situation-maritale/$',
|
||||
api_views.changement_de_situation_maritale,
|
||||
name='rsu-api-changement-de-situation-maritale'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/separation/$',
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/separation/$',
|
||||
api_views.separation, name='rsu-api-separation'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/declaration-de-deces/$',
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/declaration-de-deces/$',
|
||||
api_views.declaration_de_deces, name='rsu-api-declaration-de-deces'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/(?P<identifier_enfant>[-\w]+)/'
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/(?P<identifier_enfant>[-\w]+)/'
|
||||
'suppression-lien-de-responsabilite/$',
|
||||
api_views.suppression_lien_de_responsabilite,
|
||||
name='rsu-api-suppression-lien-de-responsabilite'),
|
||||
url(r'^individu/$', api_views.create_individu, name='rsu-api-create-individu'),
|
||||
url(r'^individu/(?P<identifier>[-\w]+)/federation/(?P<application>\w+)/$', api_views.federation,
|
||||
re_path(r'^individu/$', api_views.create_individu, name='rsu-api-create-individu'),
|
||||
re_path(r'^individu/(?P<identifier>[-\w]+)/federation/(?P<application>\w+)/$', api_views.federation,
|
||||
name='rsu-api-federation'),
|
||||
url(r'^declaration-union/$', api_views.declaration_union,
|
||||
re_path(r'^declaration-union/$', api_views.declaration_union,
|
||||
name='rsu-api-declaration-union'),
|
||||
url(r'^synchronisation/$', api_views.synchronization,
|
||||
re_path(r'^synchronisation/$', api_views.synchronization,
|
||||
name='rsu-api-synchronization'),
|
||||
|
||||
url(r'^saga/retour-asynchrone/$', api_views.saga_retour_asynchrone,
|
||||
re_path(r'^saga/retour-asynchrone/$', api_views.saga_retour_asynchrone,
|
||||
name='rsu-api-saga-retour-asynchrone'),
|
||||
url(r'^saga/retour-synchrone/$', api_views.saga_retour_synchrone,
|
||||
re_path(r'^saga/retour-synchrone/$', api_views.saga_retour_synchrone,
|
||||
name='rsu-api-saga-retour-synchrone'),
|
||||
url(r'^saga/tiers/(?P<application>\w+)/(?P<identifier>[-\w]+)/$', api_views.saga_tiers,
|
||||
re_path(r'^saga/tiers/(?P<application>\w+)/(?P<identifier>[-\w]+)/$', api_views.saga_tiers,
|
||||
name='rsu-api-saga-tiers'),
|
||||
url(r'^saga/(?P<identifier>[-\w]+)/factures/$', api_views.saga_factures,
|
||||
re_path(r'^saga/(?P<identifier>[-\w]+)/factures/$', api_views.saga_factures,
|
||||
name='rsu-api-saga-factures'),
|
||||
url(r'^saga/(?P<identifier>[-\w]+)/transaction/$', api_views.saga_transaction,
|
||||
re_path(r'^saga/(?P<identifier>[-\w]+)/transaction/$', api_views.saga_transaction,
|
||||
name='rsu-api-saga-transaction'),
|
||||
|
||||
|
||||
url(r'^qf/lire-quotients-valides/$', api_views.qf_lire_quotiens_valides,
|
||||
re_path(r'^qf/lire-quotients-valides/$', api_views.qf_lire_quotiens_valides,
|
||||
name='rsu-api-qf-lire-quotients-valides'),
|
||||
url(r'^qf/simuler/$', api_views.qf_simuler,
|
||||
re_path(r'^qf/simuler/$', api_views.qf_simuler,
|
||||
name='rsu-api-qf-simuler'),
|
||||
url(r'^qf/(?P<identifier>[-\w]+)/$', api_views.qf_calculer,
|
||||
re_path(r'^qf/(?P<identifier>[-\w]+)/$', api_views.qf_calculer,
|
||||
name='rsu-api-qf-calculer'),
|
||||
url(r'^qf/(?P<identifier>[-\w]+)/editer-carte/(?P<id_qf>\w+)/$', api_views.qf_editer_carte,
|
||||
re_path(r'^qf/(?P<identifier>[-\w]+)/editer-carte/(?P<id_qf>\w+)/$', api_views.qf_editer_carte,
|
||||
name='rsu-api-qf-editer-carte'),
|
||||
|
||||
url(r'^doublons/$', api_views.doublons,
|
||||
re_path(r'^doublons/$', api_views.doublons,
|
||||
name='rsu-api-doublons'),
|
||||
url(r'^doublons/(?P<doublon_id>[0-9 ]+)/$', api_views.doublon,
|
||||
re_path(r'^doublons/(?P<doublon_id>[0-9 ]+)/$', api_views.doublon,
|
||||
name='rsu-api-doublon'),
|
||||
url(r'^doublons/(?P<doublon_id>[0-9 ]+)/false-positive/$', api_views.false_positive,
|
||||
re_path(r'^doublons/(?P<doublon_id>[0-9 ]+)/false-positive/$', api_views.false_positive,
|
||||
name='rsu-api-doublon-false-positive'),
|
||||
url(r'^doublons/(?P<doublon_id>[0-9 ]+)/dedup/$', api_views.dedup,
|
||||
re_path(r'^doublons/(?P<doublon_id>[0-9 ]+)/dedup/$', api_views.dedup,
|
||||
name='rsu-api-doublon-dedup'),
|
||||
]
|
||||
|
|
|
@ -38,13 +38,12 @@ import psycopg2
|
|||
from django.conf import settings
|
||||
from django.contrib.postgres.search import TrigramDistance
|
||||
from django.db import connection
|
||||
from django.db.models import Q, F, Value, ExpressionWrapper, CharField, When, Case
|
||||
from django.db.models import Q, F, Value, ExpressionWrapper, CharField, When, Case, CharField
|
||||
from django.db.models.functions import Least, Greatest, Coalesce, Concat
|
||||
from django.db import transaction
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.http import HttpResponse
|
||||
|
||||
from django.utils import six
|
||||
from django.utils.timezone import now, make_aware
|
||||
from django.utils.encoding import force_bytes
|
||||
|
||||
|
@ -542,7 +541,7 @@ class PersonSearch(object):
|
|||
Coalesce(
|
||||
JSONTextRef(F('content'), 'nom_d_usage'),
|
||||
JSONTextRef(F('content'), 'nom_de_naissance'),
|
||||
Value(' ')
|
||||
Value(' '),
|
||||
),
|
||||
Value(' '),
|
||||
JSONTextRef(F('content'), 'prenoms'))
|
||||
|
@ -653,7 +652,7 @@ def integrity_check():
|
|||
def upper_dict(d):
|
||||
'''Transform all string values in d to uppercase'''
|
||||
for key, value in d.items():
|
||||
if isinstance(value, six.text_type):
|
||||
if isinstance(value, str):
|
||||
d[key] = value.upper()
|
||||
|
||||
|
||||
|
@ -1283,18 +1282,11 @@ def individu_caption(individu):
|
|||
|
||||
|
||||
def csv_export_response(rows, filename):
|
||||
if six.PY3:
|
||||
with io.StringIO(newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
for row in rows:
|
||||
writer.writerow(map(str, row))
|
||||
r = HttpResponse(f.getvalue(), content_type='text/csv')
|
||||
else:
|
||||
with io.BytesIO() as f:
|
||||
writer = csv.writer(f)
|
||||
for row in rows:
|
||||
writer.writerow(map(force_bytes, row))
|
||||
r = HttpResponse(f.getvalue(), content_type='text/csv')
|
||||
with io.StringIO(newline='') as f:
|
||||
writer = csv.writer(f)
|
||||
for row in rows:
|
||||
writer.writerow(map(str, row))
|
||||
r = HttpResponse(f.getvalue(), content_type='text/csv')
|
||||
r['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||
return r
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ from django.db import connection
|
|||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.utils.timezone import now
|
||||
from django.utils import six
|
||||
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.contrib import messages
|
||||
|
@ -117,9 +116,7 @@ def synchronize_federations_report(request, job_id, model_admin, *args, **kwargs
|
|||
if not report:
|
||||
raise Http404('no report')
|
||||
with report:
|
||||
text_report = report
|
||||
if six.PY3:
|
||||
text_report = io.TextIOWrapper(text_report, encoding='utf-8')
|
||||
text_report = io.TextIOWrapper(report, encoding='utf-8')
|
||||
reader = csv.reader(text_report)
|
||||
next(reader)
|
||||
actions = [row for row in reader if row[6] != 'KEEP']
|
||||
|
@ -152,9 +149,7 @@ def synchronize_federations_apply_report(request, job_id, model_admin, *args, **
|
|||
with report:
|
||||
if not report:
|
||||
raise Http404('no report')
|
||||
text_report = report
|
||||
if six.PY3:
|
||||
text_report = io.TextIOWrapper(text_report, encoding='utf-8')
|
||||
text_report = io.TextIOWrapper(report, encoding='utf-8')
|
||||
reader = csv.reader(text_report)
|
||||
next(reader)
|
||||
actions = [row for row in reader if row[6] != 'KEEP']
|
||||
|
|
Loading…
Reference in New Issue