Project

General

Profile

Tâche #30534

Scénario #30270: Portage python3 de l'EAD2

Erreur de "décodage" du contenu du fichier téléchargé pour l’import be1d

Added by Benjamin Bohard about 2 years ago. Updated about 2 years ago.

Status:
Fermé
Priority:
Normal
Assigned To:
Start date:
06/02/2020
Due date:
% Done:

100%

Estimated time:
0.00 h
Remaining (hours):
0.0

Description

Lors de téléchargement du fichier, le contenu est converti pour échapper tous les caractères non ascii avec la méthode d’encodage des url.
La fonction unquote considère par défaut que les caractères encodés ainsi sont en utf-8 à l’origine.
Au moment, où la méthode unquote est appelée, on ignore (en théorie) quel encodage est utilisé pour le fichier d’origine.

Associated revisions

Revision a035c5e7 (diff)
Added by Benjamin Bohard about 2 years ago

Do not use unquote in first step of importation.

Ref #30534

Revision f24e8c37 (diff)
Added by Benjamin Bohard about 2 years ago

Use unquote with trustworthy information on encoding.

Ref #30534

Revision 0bbc7465 (diff)
Added by Benjamin Bohard about 2 years ago

Code for è is c3a8 (c3a9 stands for é).

Ref #30534

Revision 7106e9ac (diff)
Added by Benjamin Bohard about 2 years ago

Raise exception if encoding is unknown.

Ref #30534

Revision a027290b (diff)
Added by Daniel Dehennin about 2 years ago

The urllib quote/unquote is not symmetric in python3

The urllib “quote” encode files differently if they are encoded
“ISO-8859-1” and “UTF-8” but “unquote” assume it was “UTF-8” by
default.

We need to use a symmetric encoding/decoding without taking care of
charset, we switch to base64.

  • frontend/web/page.py (ConnectedPage.locateChild): send uploaded file
    through XMLRPC as base64 encoded.
  • backend/actions/lib/importation/fichier.py (BaseImportationFichier._file_infos):
    decode uploaded file as base64 encoded.
  • backend/actions/lib/importation/tools.py (write_file): write content
    as bytes.

Ref: #30534

Revision 018a51e1 (diff)
Added by Daniel Dehennin about 2 years ago

be1d: decoding of uploaded file is done by EAD

  • scribe/parsing/be1d.py (parse_be1d_eleves): do only the file convert
    if required.

Ref: #30534

History

#1 Updated by Benjamin Bohard about 2 years ago

En conservant le plus longtemps possible le contenu échappé et en appelant la méthode unquote au dernier moment, lorsqu’on sait qu’on cherche les en-têtes du fichier be1d, on peut déterminer l’encodage avec plus d’assurance.

#2 Updated by Benjamin Bohard about 2 years ago

  • Status changed from Nouveau to En cours

#3 Updated by Joël Cuissinat about 2 years ago

C'est mieux, mais le test est toujours en erreur :
https://dev-eole.ac-dijon.fr/jenkins/job/2.8.0/job/test-importation-acascribe-special-2.8.0-amd64/13/console

            INFO:scribe.importation:## Lecture des élèves... ##
            ERROR:scribe.importation:local variable 'encoding' referenced before assignment

            AUTOMATE : Traceback dans la sortie console!

            Traceback (most recent call last):
              File "/usr/share/ead2/backend/bin/importation.py", line 257, in do_parse_be1d
                be1d.parse_be1d_eleves(self.store, eleve)
              File "/usr/lib/python3/dist-packages/scribe/parsing/be1d.py", line 49, in parse_be1d_eleves
                unquoted_csv = unquote(quoted_csv, encoding=encoding)
            UnboundLocalError: local variable 'encoding' referenced before assignment

#4 Updated by Daniel Dehennin about 2 years ago

Je pense que l’utilisation de chardet pourrait rendre le code plus sexy et prendre en compte plus de cas du coup :

diff --git a/scribe/parsing/be1d.py b/scribe/parsing/be1d.py
index d0561b1..2ddf9aa 100644
--- a/scribe/parsing/be1d.py
+++ b/scribe/parsing/be1d.py
@@ -16,6 +16,8 @@
 from csv import DictReader, reader
 from urllib.parse import unquote
 from sqlalchemy import and_
+from chardet import detect
+
 from scribe.eoletools import replace_cars, convert_file, formate_date, \
 formate_civilite, replace_more_cars
 from scribe.importation import log
@@ -34,23 +36,22 @@ def parse_be1d_eleves(store, csvfile):
     """ 
     num = 0
     log.infolog("Lecture des élèves...", title=True)
-    # fichier en UTF-8 ?
-    # le caractère è est encodé avec %C3%A9 en utf-8, %E8 en iso-8859-1
-    # ce qui se trouve entre le premier l et le premier v permet de déduire l’encodage.
-    with open(csvfile, 'r') as csvstream:
-        quoted_csv = csvstream.read()
-    first_l_index = quoted_csv.index('l')
-    first_v_index = quoted_csv.index('v')
-    marker = quoted_csv[first_l_index + 1: first_v_index].upper()
-    if marker == '%C3%A8':
-        encoding = 'utf-8'
-    elif marker == '%E8':
-        encoding = 'iso-8859-1'
-    else:
-        raise Exception("L’encodage du fichier source n’a pas pu être déterminé")
-    unquoted_csv = unquote(quoted_csv, encoding=encoding)
+
+    # Detect file encoding
+    with open(csvfile, 'rb') as csvstream:
+        sample = csvstream.read(4096)
+        encoding_detection = detect(sample)
+        if encoding_detection['confidence'] < 0.5:
+            raise Exception(f"L’encodage du fichier source n’a pas pu être déterminé")
+
+    with open(csvfile, 'r', encoding=encoding_detection['encoding']) as csvstream:
+      quoted_csv = csvstream.read()
+
+    unquoted_csv = unquote(quoted_csv, encoding=encoding_detection['encoding'])
+
     with open(csvfile, 'w') as csvstream:
         csvstream.write(unquoted_csv)
+
     source = 'onde'
     # champs obligatoires
     onde_fields = ['Nom élève', 'Prénom élève', 'Date naissance', 'Sexe', 'INE',

La demande est toujours en cours, puis-je pousser ma modification ?

#5 Updated by Daniel Dehennin about 2 years ago

Cet encodage est fait par nous même dans l’EAD pour faire passer le contenu du service web au backend EAD par XMLRPC.

J’ai finalement troqué quote/unquote de urllib, qui est normalement prévu pour l’encodage d’URL, par un encodage base64.

Tout ceci se fait dans l’EAD et ne doit pas impacter les routines d’importation Scribe.

#6 Updated by Joël Cuissinat about 2 years ago

  • Assigned To set to Daniel Dehennin

#7 Updated by Joël Cuissinat about 2 years ago

  • Status changed from En cours to Résolu

#8 Updated by Joël Cuissinat about 2 years ago

  • Status changed from Résolu to Fermé
  • Remaining (hours) set to 0.0

Le test Jenkins plante toujours mais plus loin :o (#30584)
J'ai testé un import de fichier par l'EAD sans erreur.

#9 Updated by Joël Cuissinat about 2 years ago

  • % Done changed from 0 to 100
  • Estimated time set to 0.00 h

Also available in: Atom PDF