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
100%
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.
Révisions associées
Do not use unquote in first step of importation.
Ref #30534
Use unquote with trustworthy information on encoding.
Ref #30534
Code for è is c3a8 (c3a9 stands for é).
Ref #30534
Raise exception if encoding is unknown.
Ref #30534
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
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
Historique
#1 Mis à jour par Benjamin Bohard il y a plus de 3 ans
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 Mis à jour par Benjamin Bohard il y a plus de 3 ans
- Statut changé de Nouveau à En cours
#3 Mis à jour par Joël Cuissinat il y a plus de 3 ans
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 Mis à jour par Daniel Dehennin il y a plus de 3 ans
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 Mis à jour par Daniel Dehennin il y a plus de 3 ans
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 Mis à jour par Joël Cuissinat il y a plus de 3 ans
- Assigné à mis à Daniel Dehennin
#7 Mis à jour par Joël Cuissinat il y a plus de 3 ans
- Statut changé de En cours à Résolu
#8 Mis à jour par Joël Cuissinat il y a plus de 3 ans
- Statut changé de Résolu à Fermé
- Restant à faire (heures) mis à 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 Mis à jour par Joël Cuissinat il y a plus de 3 ans
- % réalisé changé de 0 à 100
- Temps estimé mis à 0.00 h