Skip to content

Commit

Permalink
fix: Champs jsonb dans les attributs des modèles
Browse files Browse the repository at this point in the history
Correction de l'issue #178 .

Modification des fonctions `plume.pg.queries.query_insert_or_update_any_table` et `plume.pg.queries.query_update_any_table` pour qu'elles appliquent la nouvelle fonction `plume.rdf.utils.flatten_values`, qui encode en JSON les valeurs de type dictionnaire, car psycopg2 ne fait pas de lui-même la conversion dans ce sens.
  • Loading branch information
alhyss committed Dec 15, 2023
1 parent 3983bb0 commit 56d4e96
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 1 deletion.
5 changes: 5 additions & 0 deletions plume/pg/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from psycopg2 import sql
from psycopg2.extras import Json

from plume.rdf.utils import flatten_values
from plume.rdf.exceptions import UnknownParameterValue, MissingParameter
from plume.rdf.namespaces import PLUME
from plume.config import PLUME_PG_MIN_VERSION, PLUME_PG_MAX_VERSION
Expand Down Expand Up @@ -1569,6 +1570,8 @@ def query_insert_or_update_any_table(
else:
raise TypeError("'data' devrait être un dictionnaire ou une liste")

values = flatten_values(values)

return PgQueryWithArgs(
query = sql.SQL(
"""
Expand Down Expand Up @@ -1660,6 +1663,8 @@ def query_update_any_table(schema_name, table_name, pk_name, data, columns=None)
columns = [k for k in data.keys()]
else:
raise TypeError("'data' devrait être un dictionnaire ou une liste")

values = flatten_values(values)

pk_index = columns.index(pk_name)
if pk_index is not None:
Expand Down
11 changes: 11 additions & 0 deletions plume/pg/tests/queries_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,16 @@ def test_query_insert_or_update_meta_categorie(self):
)
cur.execute(*query)
result3 = cur.fetchone()
query = query_insert_or_update_meta_categorie(
{
'path': 'dct:title',
'origin': 'shared',
'compute': ['new'],
'compute_params': {'flags': 'gi', 'pattern': '(t(?:\\s|\\w)+)[.]'}
}
)
cur.execute(*query)
result4 = cur.fetchone()
cur.execute('''
DROP EXTENSION plume_pg ; CREATE EXTENSION plume_pg
''')
Expand All @@ -1380,6 +1390,7 @@ def test_query_insert_or_update_meta_categorie(self):
self.assertIsNotNone(result3)
self.assertEqual(result3[0]['label'], 'Nom')
self.assertEqual(result3[0]['description'], 'Nom Nom Nom')
self.assertEqual(result4[0]['compute_params'], {'flags': 'gi', 'pattern': '(t(?:\\s|\\w)+)[.]'})

def test_query_read_enum_meta_special(self):
"""Récupération du type énuméré des champs 'special'.
Expand Down
69 changes: 69 additions & 0 deletions plume/pg/tests/template_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,75 @@ def test_choose_template_without_priority(self):
search_template(templates),
'Bon modèle sans priorité'
)

def test_dump_load_template_with_json_field(self):
"""Export et ré-import d'un modèle avec un attribut JSON."""
pfile = Path(abspath('pg/tests/export/template_data_json_temp.json'))
conn = psycopg2.connect(TemplateTestCase.connection_string)
with conn:
with conn.cursor() as cur:
cur.execute('SELECT * FROM z_plume.meta_import_sample_template()')
cur.execute(r'''
UPDATE z_plume.meta_categorie
SET compute = ARRAY['new'],
compute_params = '{"pattern": "(t(?:\\s|\\w)+)[.]", "flags": "gi"}'::jsonb
WHERE path = 'dct:title' ;
''')
cur.execute(
*query_read_meta_template()
)
templates = cur.fetchall()
cur.execute(
*query_read_meta_categorie()
)
categories = cur.fetchall()
cur.execute(
*query_read_meta_tab()
)
tabs = cur.fetchall()
cur.execute(
*query_read_meta_template_categories()
)
template_categories = cur.fetchall()

cur.execute(
"SELECT tpl_id FROM z_plume.meta_template WHERE tpl_label = 'Basique'"
)
tpl_id = cur.fetchone()[0]
cur.execute('DROP EXTENSION plume_pg ; CREATE EXTENSION plume_pg')
conn.close()

dump_template_data(
pfile,
templates=templates,
categories=categories,
tabs=tabs,
template_categories=template_categories,
tpl_id=tpl_id
)
builder = TemplateQueryBuilder(pfile)
pfile.unlink()

conn = psycopg2.connect(TemplateTestCase.connection_string)
with conn:
with conn.cursor() as cur:

for query in builder.queries():
cur.execute(*query)
if builder.waiting:
result = cur.fetchone()[0]
builder.feedback(result)

cur.execute(r'''
SELECT compute_params = '{"pattern": "(t(?:\\s|\\w)+)[.]", "flags": "gi"}'::jsonb
FROM z_plume.meta_categorie
WHERE path = 'dct:title' ;
''')
res = cur.fetchone()[0]

cur.execute('DROP EXTENSION plume_pg ; CREATE EXTENSION plume_pg')
conn.close()
self.assertTrue(res)

if __name__ == '__main__':
unittest.main()
Expand Down
9 changes: 8 additions & 1 deletion plume/rdf/tests/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
date_from_str, time_from_str, str_from_decimal, decimal_from_str,
main_datatype, export_format_from_extension, export_formats, no_logging,
MetaCollection, almost_included, all_words_included, text_with_link,
langstring_from_str
langstring_from_str, flatten_values
)
from plume.rdf.namespaces import PlumeNamespaceManager, DCT, XSD, RDF

Expand Down Expand Up @@ -572,6 +572,13 @@ def test_langstring_from_str(self):
self.assertIsNone(langstring_from_str('abc', '?'))
self.assertEqual(langstring_from_str('abc', 'fr'), Literal('abc', lang='fr'))

def test_flatten_values(self):
"""Encode en JSON des dictionnaires imbriqués."""
self.assertListEqual(
flatten_values([1, 'abc', [1, 2, 3], {'d.1': 'x', 'd.2': False}]),
[1, 'abc', [1, 2, 3], '{"d.1": "x", "d.2": false}']
)

if __name__ == '__main__':
unittest.main()

21 changes: 21 additions & 0 deletions plume/rdf/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
import re
import logging
import json
from pathlib import Path
from uuid import UUID, uuid4
from html import escape
Expand Down Expand Up @@ -1451,6 +1452,26 @@ def all_words_included(included, including):
including_words = including.split('-')
return all(w in including_words for w in included_words)

def flatten_values(values):
"""Renvoie une copie de la liste en encodant en JSON les valeurs de type dictionnaire.
Parameters
----------
values : list
Une liste de valeurs de types arbitraires.
Returns
-------
list
"""
new_list = []
for value in values:
if isinstance(value, dict):
value = json.dumps(value, ensure_ascii=False)
new_list.append(value)
return new_list

class MetaCollection(type):
"""Méta-classe gérant l'accès à une collection d'objets.
Expand Down

0 comments on commit 56d4e96

Please sign in to comment.