Open alhyss opened 2 years ago
j'ai testé ce code comme tu l'as bien expliqué ci-dessus, mais il se trouve que je rencontre des erreurs, qui semblent provenir du format de l'object table.sql: "expected string or bytes-like object" Est-ce que tu as testé ce code sur ton environnement ? `
for table_name, table in vocab_data.items():
if not table.sql:
logging.info(
'No SQL definition available for ' f'table "{table_name}" of vocabulary "{name}".')
continue
__create_table_and_load_data(
table_name=table_name,
table_schema=table.sql,
data=vocab_data
) ``
@asboukerram Non, je n'ai pas testé ces lignes. Mais l'erreur est surprenante, considérant que si l'attribut sql
n'est pas None
il s'agit nécessairement d'un objet sqlalchemy.Table
comme l'était auparavant la variable _table
. Si vous n'avez pas déjà réglé le problème, est-ce qu'il serait possible d'avoir le détail de l'erreur ?
l'erreur de produit à la ligne
table_creation_sql = CreateTable(table_schema)
la fontion CreateTable ne reconnait pas le format de l'object reçu en paramètre et lève une exception: expected "string or bytes-like object"
j'ai aussi parfois cette erreur, il faut que je relance le conteneur que cette erreur disparaisse:
Exception in thread Thread-13:
ckan-mte-ckan-dev-1 | Traceback (most recent call last):
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
ckan-mte-ckan-dev-1 | self.run()
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/dcat/plugin.py", line 415, in run
ckan-mte-ckan-dev-1 | 2022-12-12 13:52:20,255 INFO [ckan.config.middleware.flask_app] 200 /api/load-vocab render time 0.014 seconds
ckan-mte-ckan-dev-1 | load_all_vocab(vocab_list=vocab_list)
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/vocabulary/loader.py", line 203, in load_vocab
ckan-mte-ckan-dev-1 | vocab_data=VocabularyIndex.load(name).data
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/vocabulary/index.py", line 294, in load
ckan-mte-ckan-dev-1 | return parser.__call__(**params)
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/vocabulary/parser/parsers.py", line 217, in basic_rdf
ckan-mte-ckan-dev-1 | result = _result if _result is not None else VocabularyParsingResult(name)
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/vocabulary/parser/result.py", line 58, in __init__
ckan-mte-ckan-dev-1 | self._data = VocabularyDataCluster(vocabulary)
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/vocabulary/parser/model.py", line 1091, in __init__
ckan-mte-ckan-dev-1 | table = VocabularyLabelTable(
ckan-mte-ckan-dev-1 | File "/srv/app/src_extensions/ckanext-ecospheres/ckanext/ecospheres/vocabulary/parser/model.py", line 692, in __init__
ckan-mte-ckan-dev-1 | self.sql = sqlalchemy.Table(
ckan-mte-ckan-dev-1 | File "<string>", line 2, in __new__
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/util/deprecations.py", line 130, in warned
ckan-mte-ckan-dev-1 | return fn(*args, **kwargs)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 496, in __new__
ckan-mte-ckan-dev-1 | metadata._remove_table(name, schema)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 68, in __exit__
ckan-mte-ckan-dev-1 | compat.reraise(exc_type, exc_value, exc_tb)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/util/compat.py", line 154, in reraise
ckan-mte-ckan-dev-1 | raise value
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 491, in __new__
ckan-mte-ckan-dev-1 | table._init(name, metadata, *args, **kw)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 590, in _init
ckan-mte-ckan-dev-1 | self._init_items(*args)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 107, in _init_items
ckan-mte-ckan-dev-1 | item._set_parent_with_dispatch(self)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/base.py", line 456, in _set_parent_with_dispatch
ckan-mte-ckan-dev-1 | self._set_parent(parent)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 1507, in _set_parent
ckan-mte-ckan-dev-1 | self._setup_on_memoized_fks(lambda fk: fk._set_remote_table(table))
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 1518, in _setup_on_memoized_fks
ckan-mte-ckan-dev-1 | fn(fk)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 1507, in <lambda>
ckan-mte-ckan-dev-1 | self._setup_on_memoized_fks(lambda fk: fk._set_remote_table(table))
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 2059, in _set_remote_table
ckan-mte-ckan-dev-1 | self._link_to_col_by_colstring(parenttable, table, colname)
ckan-mte-ckan-dev-1 | File "/usr/lib/python3.8/site-packages/sqlalchemy/sql/schema.py", line 1952, in _link_to_col_by_colstring
ckan-mte-ckan-dev-1 | assert self.constraint._referred_table is table
ckan-mte-ckan-dev-1 | AssertionError
Merci @asboukerram
Concernant la première erreur.
Il y a bien une coquille dans l'extrait de code en question. Il faut l'écrire comme ci-après pour ne pas avoir une erreur TypeError: Boolean value of this clause is not defined
sur la commande if not table.sql:
.
for table_name, table in vocab_data.items():
if table.sql is None:
logging.info(
'No SQL definition available for '
f'table "{table_name}" of vocabulary "{name}".'
)
continue
__create_table_and_load_data(
table_name=table_name,
table_schema=table.sql,
data=vocab_data
)
À corriger, donc, si vous ne l'aviez pas déjà fait, mais ce n'est pas la même erreur que celle que vous indiquez.
Sinon j'ai testé ça, qui fonctionne et confirme ainsi qu'il est bien possible d'exécuter CreateTable
sur les valeurs de l'attribut sql
:
from sqlalchemy.schema import CreateTable
from ckanext.ecospheres.vocabulary.index import VocabularyIndex
queries = []
for name in VocabularyIndex.names():
if not name in ('insee_official_geographic_code',):
result = VocabularyIndex.load(name)
if not result:
print(f'critical failure for "{name}"')
continue
for table_name, table in result.data.items():
if table.sql is None:
print(f'missing sql definition for "{table_name}"')
continue
queries.append(CreateTable(table.sql))
print(f'"{table_name}" ok')
Comme vous pouvez le voir, j'ai exclu le vocabulaire des codes géographiques parce que c'est trop long. Est-ce que l'erreur apparaissait spécifiquement pour ce vocabulaire ?
Par ailleurs, même si CreateTable
recevait un argument du mauvais type (j'ai essayé avec None
, un entier et une chaîne de caractères), l'erreur qui apparaît est AttributeError: 'int' object has no attribute 'columns'
, pas expected string or bytes-like object
. Et, encore une fois, il est par construction impossible que la valeur de l'attribut sql
soit autre chose que None
ou un objet sqlalchemy.Table
- et c'est ce dernier type qui est attendu, pas des objets str
ou bytes
(qui provoquent la même erreur AttributeError: 'int' object has no attribute 'columns'
).
Bref, il semblerait que le problème soit ailleurs ?
Ce n'est peut-être pas ça, mais l'un des cas commun d'apparition de cette erreur TypeError: expected string or bytes-like object
est l'application d'une des fonctions du module re
sur None
ou autre chose qu'un objet str
ou bytes
.
Notez que l'URL d'accès aux données des référentiels de coordonnées IGN (vocabulaire ign_crs
) ne fonctionnait plus, et la structure des données a changé pour le vocabulaire inspire_theme
. Je viens de faire les modifications nécessaires sur la branche spatial-for-merge
. Si vous vous référez à ce que j'expliquais plus haut sur la gestion des erreurs, il s'agissait de deux cas où la valeur booléenne de result
aurait été False
/ result.status_code
vaut 1
/ result.data
vaut None
. Il devrait s'agir d'une situation où les erreurs sont maîtrisées, mais, ce faisant, je me suis rendue compte que j'avais parlé partout de tester la valeur booléenne des objets VocabularyParsingResult
pour détecter les erreurs critiques, alors que jusque-là c'était la valeur booléenne de l'attribut VocabularyParsingResult.status_code
qui donnait cette information... J'ai changé ça : maintenant la valeur booléenne du VocabularyParsingResult
est identique à celle de son attribut VocabularyParsingResult.status_code
et tout ce que j'avais indiqué fonctionne. Il est possible que cette modification suffise à régler le problème.
Concerne le module
ckanext.ecospheres.vocabulary.loader
, qui n'existe que sur la branchedev
.Pour le module
ckanext.ecospheres.vocabulary.index
, le fichiervocabularies.yaml
et tous les modules deckanext.ecospheres.vocabulary.parser
, les versions de référrence sont celles de la branchespatial
.Schéma PostgreSQL
Il paraîtrait préférable que les vocabulaires aient un espace de nommage/schéma distinct du reste des données de PostgreSQL.
Pour l'heure, le nom de ce schéma est défini par la constante
ckanext.ecospheres.vocabulary.parser.model.SQL_SCHEMA
et il s'agit de'vocabulary'
. Il pourrait être envisagé d'en faire un paramètre de configuration. Je suis partie du principe que ce schéma existera lorsque j'ai mis en place ce que j'évoque ci-après.À noter qu'il faudra peut-être ajouter la création de ce schéma à la liste des commandes à passer pour préparer la base de données lors de l'installation de CKAN (avec
CREATE
etUSAGE
pour le rôleckan
) si le rôleckan
n'a pasCREATE
sur la base.Fonction à utiliser pour l'import et la normalisation des vocabulaires
Pour mémoire, c'est le module
ckanext.ecospheres.vocabulary.index
qui fournit les méthodes à utiliser en amont pour récupérer les vocabulaires et les normaliser avant chargement en base. L'en-tête du fichier explique leur fonctionnement. Je l'ai beaucoup précisée ces derniers jours.La fonction
load_vocab
du moduleloader
utilise actuellement la méthodeVocabularyIndex.load_and_dump
, qui - en plus d'importer et harmoniser les vocabulaires - en fait des exports JSON stockés en local. Ces exports ne présentent pas un grand intérêt en production.Il serait préférable d'utiliser simplement la méthode
VocabularyIndex.load
.Gestion des erreurs
Actuellement,
load_vocab
se contente de regarder si l'attributdata
du résultat renvoyé parVocabularyIndex.load_and_dump
n'est pas vide et génère une erreur générique si c'est le cas. C'est dommage, car les objetsckanext.ecospheres.vocabulary.parser.result.VocabularyParsingResult
renvoyés parVocabularyIndex.load
etVocabularyIndex.load_and_dump
fournissent beaucoup plus d'informations sur les problèmes rencontrés.La valeur booléenne du
VocabularyParsingResult
indique si une erreur critique a eu lieu, c'est-à-dire une erreur qui a complètement empêché la récupération du vocabulaire. Elle vautTrue
si et seulement si le résultat est exploitable. Dans ce cas, l'attributdata
sera toujours un cluster de données exploitable, alors qu'il vautNone
en cas d'erreur critique.L'attribut
status_code
duVocabularyParsingResult
est plus précis. Sa valeur entière est0
en l'absence de toute erreur,1
en cas d'erreur critique,2
s'il y a eu des erreurs, mais aucune erreur critique.L'attribut
log
liste les erreurs rencontrées à proprement parler (sous classes d'Exception
). Dès lors questatus_code
n'est pas0
, cette liste ne sera par construction jamais vide. En cas d'erreur critique, celle-ci est toujours la dernière de la liste, car elle stoppe l'exécution.On peut imaginer une analyse du résultat de ce type :
Connaissance de la structure des tables nécessaire à leur création
Désormais, les pseudo-tables listées par l'attribut
VocabularyParsingResult.data
ont un attributsql
qui :None
, et dans ce cas il ne faut pas tenter de mettre la table en base, et bien entendu le tracer dans les logs. Pour mémoire, cela signifie qu'il manque une sous-classe deckanext.ecospheres.vocabulary.parser.model.VocabularyDataTable
pour le type de table considéré.sqlalchemy.sql.schema.Table
qui peut être directement utilisé pour créer la table en base.Autrement dit,
load_vocab
ne fera plus :mais simplement :