From 5e0536623d1a439c762b0cdc85a8f7ba7e201ec4 Mon Sep 17 00:00:00 2001 From: Koen Van Daele Date: Thu, 2 Jul 2020 00:03:04 +0200 Subject: [PATCH 1/5] Set forced language display. Refs #561 * Set forced display language for species. Refs #561 * Update docs for forced label generation. Refs #561 --- .../+package+/skos/__init__.py_tmpl | 3 +- atramhasis/skos/__init__.py | 3 +- docs/source/customisation.rst | 61 +++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/atramhasis/scaffolds/atramhasis_demo/+package+/skos/__init__.py_tmpl b/atramhasis/scaffolds/atramhasis_demo/+package+/skos/__init__.py_tmpl index 25c5e99e..f3337cd0 100644 --- a/atramhasis/scaffolds/atramhasis_demo/+package+/skos/__init__.py_tmpl +++ b/atramhasis/scaffolds/atramhasis_demo/+package+/skos/__init__.py_tmpl @@ -126,7 +126,8 @@ def create_registry(request): 'created': [date(2011,5,23)], 'language': ['nl-BE', 'la'], 'license': LICENSES - } + }, + 'atramhasis.force_display_label_language': 'la' }, request.db, uri_generator=UriPatternGenerator('https://id.erfgoed.net/thesauri/soorten/%s') diff --git a/atramhasis/skos/__init__.py b/atramhasis/skos/__init__.py index 0a9103a2..eeb8fc69 100644 --- a/atramhasis/skos/__init__.py +++ b/atramhasis/skos/__init__.py @@ -137,7 +137,8 @@ def create_registry(request): 'created': [date(2011, 5, 23)], 'language': ['nl-BE', 'la'], 'license': LICENSES - } + }, + 'atramhasis.force_display_label_language': 'la' }, request.db, uri_generator=UriPatternGenerator('https://id.erfgoed.net/thesauri/soorten/%s') diff --git a/docs/source/customisation.rst b/docs/source/customisation.rst index b96bd271..cffe8059 100644 --- a/docs/source/customisation.rst +++ b/docs/source/customisation.rst @@ -294,6 +294,67 @@ calls to this conceptscheme will function as normal and you will be able to maintain it from the admin interface. +.. _force_display_label_language: + +Force a display language for a vocabulary +========================================= + +Under normal cicrumstances, Atramhasis tries to provide the most +appropriate label for a certain concept or collection, based on some default +configuration and the preferences of the end-user. Every provider can be marked +as having a certain `default language` (English if not set), but Atramhasis +also tries to read what the user wants. It does this through the user's +browser's locale. This information can be read from the browser's HTTP headers +or cookies. Generally, Atramhasis just knows in what language a user is +browsing the site and tries to return labels appropriate for that language. So, +the same thesaurus visited from the US will return English labels, while it +will return Dutch when visited from Gent (Belgium). + +You might have a vocabulary with a strongly preferential relation to a certain +language. We ran into this situation with a vocabulary of species: names for +plants and trees commonly found in Flanders. Some of them have one or more +local, Dutch, names. Most or all of them have an official name in Latin. The +normal language handling mechanism created a weird situation. It led to a tree +of names that was mostly in Latin, with the odd Dutch word thrown in for good +measure. This was not as desired by our users. To that end, a special mechanism +was created to force rendering labels of concepts and collections in a certain +language, no matter what the end-user's browser is requesting. + +To set this, please edit the :file:`my_thesaurus/skos/__init__.py`. Look for the +thesaurus you want to override and add a setting `atramhasis.force_display_label_language` +to the provider's metadata. Set it to a language supported by the provider +(there's little sense to setting it to a language that isn't present in the +vocabulary). Now Atramhasis will try serving concepts from this provider with +this language. All labels will still be shown, but the page title or current +label will be set to the selected language as much as possible. The normal +language determination mechanisms will keep on working, so if the concept has +no label in the requested language, Atramhasis will fall back on other labels +present. + +Your provider should end up similar to this: + +.. code-block:: python + + STUFF = SQLAlchemyProvider( + { + 'id': 'STUFF', + 'conceptscheme_id': 1, + 'atramhasis.force_display_label_language': 'la' + }, + request.db, + uri_generator=UriPatternGenerator( + 'http://id.mydata.org/thesauri/stuff/%s' + ) + ) + +Beware that this will only affect the Atramhasis UI, not the Atramhasis REST +services. We looked into some solutions for our problem that would have also +changed the underlying service, but decided against that because it would have +prevented you from making your own choices when interacting with Atramhasis. If +you want to render the tree of concepts using a preferred language different +from what a browser would advocate for, you can pass the language parameter in +a url, eg. `http://my.thesaurus.org/conceptschemes/STUFF/tree?language=la`. + .. _i18n: Internationalisation From e6aa9d071518caa873bb91f4aa3c79dd828c726d Mon Sep 17 00:00:00 2001 From: Wim De Clercq Date: Mon, 13 Jul 2020 13:50:20 +0200 Subject: [PATCH 2/5] Add language parameter for the tree view. (#571) Closes-Issue: #560 --- atramhasis/views/views.py | 6 +++--- tests/test_functional.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/atramhasis/views/views.py b/atramhasis/views/views.py index 044a5b79..058c0d30 100644 --- a/atramhasis/views/views.py +++ b/atramhasis/views/views.py @@ -255,8 +255,8 @@ def results_csv(self): @view_config(route_name='scheme_tree', renderer='json', accept='application/json') def results_tree_json(self): scheme_id = self.request.matchdict['scheme_id'] - locale = self.request.locale_name - dicts = self.get_results_tree(scheme_id, locale) + language = self.request.params.get('language', self.request.locale_name) + dicts = self.get_results_tree(scheme_id, language) if dicts: return dicts else: @@ -282,7 +282,7 @@ def get_scheme(self, scheme, locale): def parse_thing(self, thing, parent_tree_id): tree_id = self.create_treeid(parent_tree_id, thing.concept_id) - locale = self.request.locale_name + locale = self.request.params.get('language', self.request.locale_name) if thing.type and thing.type == 'collection': cs = [member for member in thing.members] if hasattr(thing, 'members') else [] diff --git a/tests/test_functional.py b/tests/test_functional.py index 4daae10a..cf6eb16d 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -520,6 +520,22 @@ def test_missing_labels(self): self.assertEqual('label', response.json[0]['label']) self.assertEqual(None, response.json[1]['label']) + def test_tree_language(self): + response = self.testapp.get('/conceptschemes/TREES/tree?language=nl', + headers=self._get_default_headers()) + self.assertEqual(200, response.status_code) + self.assertEqual( + ['De Lariks', 'De Paardekastanje'], + [child['label'] for child in response.json[0]['children']] + ) + response = self.testapp.get('/conceptschemes/TREES/tree?language=en', + headers=self._get_default_headers()) + self.assertEqual(200, response.status_code) + self.assertEqual( + ['The Chestnut', 'The Larch'], + [child['label'] for child in response.json[0]['children']] + ) + def test_no_tree(self): response = self.testapp.get('/conceptschemes/FOO/tree?_LOCALE_=nl', headers=self._get_default_headers(), status=404, expect_errors=True) From f05592092659f40fe67b069e353cd70f40d95020 Mon Sep 17 00:00:00 2001 From: Tim Van Campenhout <45393594+vancamti@users.noreply.github.com> Date: Wed, 15 Jul 2020 17:05:19 +0200 Subject: [PATCH 3/5] Feature/epic 379/562 overriding labels (#580) * #562 overriding labels * #562 hopefully improves coverage * #562 fix locale * #562 review fix v1 --- atramhasis/templates/concept.jinja2 | 18 +++++++++--------- atramhasis/templates/macros.jinja2 | 6 +++--- atramhasis/templates/tree.jinja2 | 6 +++--- atramhasis/views/views.py | 15 ++++++++++----- tests/test_views.py | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/atramhasis/templates/concept.jinja2 b/atramhasis/templates/concept.jinja2 index 7e0182c6..fc6b3e47 100644 --- a/atramhasis/templates/concept.jinja2 +++ b/atramhasis/templates/concept.jinja2 @@ -37,7 +37,7 @@ {% if concept %}
-

{{ concept.label(request.locale_name).label|title }}

+

{{ concept.label(locale).label|title }}

[ ID : {{ concept.concept_id }} ]


@@ -105,10 +105,10 @@

{% trans %}broader{% endtrans %}

{% if concept.broader_concepts|length > 0 %} - {{ render_relaties_lijst(request, concept.broader_concepts, scheme_id) }} + {{ render_relaties_lijst(request, concept.broader_concepts, scheme_id, locale) }} {% endif %} {% if concept.member_of|length > 0 %} - {{ render_relaties_lijst(request, concept.member_of, scheme_id) }} + {{ render_relaties_lijst(request, concept.member_of, scheme_id, locale) }} {% endif %}
{% endif %} @@ -118,10 +118,10 @@

{% trans %}narrower{% endtrans %}

{% if concept.narrower_concepts|length > 0 %} - {{ render_relaties_lijst(request, concept.narrower_concepts, scheme_id) }} + {{ render_relaties_lijst(request, concept.narrower_concepts, scheme_id, locale) }} {% endif %} {% if concept.narrower_collections|length > 0 %} - {{ render_relaties_lijst(request, concept.narrower_collections, scheme_id) }} + {{ render_relaties_lijst(request, concept.narrower_collections, scheme_id, locale) }} {% endif %}
{% endif %} @@ -130,7 +130,7 @@ {% if concept.related_concepts|length > 0 %}

{% trans %}related{% endtrans %}

- {{ render_relaties_lijst(request, concept.related_concepts, scheme_id) }} + {{ render_relaties_lijst(request, concept.related_concepts, scheme_id, locale) }}
{% endif %} {% endif %} @@ -141,10 +141,10 @@

{% trans %}broader{% endtrans %}

{% if concept.broader_concepts|length > 0 %} - {{ render_relaties_lijst(request, concept.broader_concepts, scheme_id) }} + {{ render_relaties_lijst(request, concept.broader_concepts, scheme_id, locale) }} {% endif %} {% if concept.member_of|length > 0 %} - {{ render_relaties_lijst(request, concept.member_of, scheme_id) }} + {{ render_relaties_lijst(request, concept.member_of, scheme_id, locale) }} {% endif %}
{% endif %} @@ -152,7 +152,7 @@ {% if concept.members|length > 0 %}

{% trans %}narrower{% endtrans %}

- {{ render_relaties_lijst(request, concept.members, scheme_id) }} + {{ render_relaties_lijst(request, concept.members, scheme_id, locale) }}
{% endif %} {% endif %} diff --git a/atramhasis/templates/macros.jinja2 b/atramhasis/templates/macros.jinja2 index 0e8f94ef..c274b9f1 100644 --- a/atramhasis/templates/macros.jinja2 +++ b/atramhasis/templates/macros.jinja2 @@ -1,11 +1,11 @@ -{% macro render_relaties_lijst(request, relaties, scheme_id) %} +{% macro render_relaties_lijst(request, relaties, scheme_id, locale) %} {% if relaties|length > 0 %} {%- set counter = 0 %}
- {%- for c in relaties|label_sort(language=request.locale_name) %} + {%- for c in relaties|label_sort(language=locale) %} {%- set counter = counter + 1 %} {%- endfor %}
diff --git a/atramhasis/templates/tree.jinja2 b/atramhasis/templates/tree.jinja2 index 4d306481..80b17665 100644 --- a/atramhasis/templates/tree.jinja2 +++ b/atramhasis/templates/tree.jinja2 @@ -51,7 +51,7 @@ {% endif %} {% if concept %} - var scheme_label = '{{ get_conceptscheme_label(concept.conceptscheme, request.locale_name)|trim }}'; + var scheme_label = '{{ get_conceptscheme_label(concept.conceptscheme, locale)|trim }}'; var scheme_id = '{{ scheme_id }}'; var current = '{{ concept.concept_id }}'; {% else %} @@ -59,8 +59,8 @@ var scheme_id = '{{ conceptscheme.scheme_id }}'; var current = '{{ conceptscheme.scheme_id }}'; {% endif %} - - d3.json('{{ request.route_path("scheme_tree", scheme_id = scheme_id ) }}', function(error, tree) { + d3.json('{{ request.route_path("scheme_tree", scheme_id = scheme_id, _query={'language':locale}) }}', + function (error, tree) { if (error) throw error; var treeData = { 'label': scheme_label, 'scheme_id': scheme_id, children: tree } diff --git a/atramhasis/views/views.py b/atramhasis/views/views.py index 058c0d30..ce0e57e1 100644 --- a/atramhasis/views/views.py +++ b/atramhasis/views/views.py @@ -125,10 +125,11 @@ def conceptscheme_view(self): 'uri': conceptscheme.uri, 'labels': conceptscheme.labels, 'notes': conceptscheme.notes, - 'top_concepts': provider.get_top_concepts() + 'top_concepts': provider.get_top_concepts(), } - return {'conceptscheme': scheme, 'conceptschemes': conceptschemes} + return {'conceptscheme': scheme, 'conceptschemes': conceptschemes, + 'locale': self.request.locale_name} @audit @view_config(route_name='concept', renderer='atramhasis:templates/concept.jinja2') @@ -146,9 +147,12 @@ def concept_view(self): scheme_id = self.request.matchdict['scheme_id'] c_id = self.request.matchdict['c_id'] provider = self.request.skos_registry.get_provider(scheme_id) - if not provider: raise ConceptSchemeNotFoundException(scheme_id) + if 'atramhasis.force_display_label_language' in provider.metadata: + locale = provider.metadata['atramhasis.force_display_label_language'] + else: + locale = self.request.locale_name try: c = self.skos_manager.get_thing(c_id, provider.conceptscheme_id) if isinstance(c, Concept): @@ -160,7 +164,8 @@ def concept_view(self): url = self.request.route_url('concept', scheme_id=scheme_id, c_id=c_id) update_last_visited_concepts(self.request, {'label': c.label(self.request.locale_name).label, 'url': url}) return {'concept': c, 'conceptType': concept_type, 'scheme_id': scheme_id, - 'conceptschemes': conceptschemes, 'provider': provider} + 'conceptschemes': conceptschemes, 'provider': provider, + 'locale': locale} except NoResultFound: raise ConceptNotFoundException(c_id) @@ -255,7 +260,7 @@ def results_csv(self): @view_config(route_name='scheme_tree', renderer='json', accept='application/json') def results_tree_json(self): scheme_id = self.request.matchdict['scheme_id'] - language = self.request.params.get('language', self.request.locale_name) + language = self.request.params.get('language') or self.request.locale_name dicts = self.get_results_tree(scheme_id, language) if dicts: return dicts diff --git a/tests/test_views.py b/tests/test_views.py index 87067295..d9c70fa7 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -32,6 +32,7 @@ def provider(some_id): provider_mock.get_metadata = Mock(return_value={'id': some_id, 'subject': []}) provider_mock.allowed_instance_scopes = ['single', 'threaded_thread'] provider_mock.conceptscheme_id = Mock(return_value=some_id) + provider_mock.metadata={} return provider_mock @@ -285,6 +286,19 @@ def test_passing_view(self): self.assertEqual(info['conceptType'], 'Concept') self.assertEqual(info['scheme_id'], 'TREES') + def test_passing_view_with_languague(self): + request = self.request + request.matchdict['scheme_id'] = 'TREES' + request.matchdict['c_id'] = '1' + request.skos_registry = self.regis + request.skos_registry.providers['TREES'].metadata = { + 'atramhasis.force_display_label_language': 'nl' + } + atramhasisview = AtramhasisView(request) + info = atramhasisview.concept_view() + self.assertIsNotNone(info['concept']) + self.assertEqual(info['locale'], 'nl') + def test_passing_collection_view(self): request = self.request request.matchdict['scheme_id'] = 'TREES' From 3497f791a9837caf83811258f2ef69275fab6c19 Mon Sep 17 00:00:00 2001 From: Tim Van Campenhout <45393594+vancamti@users.noreply.github.com> Date: Thu, 16 Jul 2020 13:35:30 +0200 Subject: [PATCH 4/5] Feature/epic 379/563 overriding labels (#581) * #562 overriding labels * #562 hopefully improves coverage * #562 fix locale * #563 override labels * #562 overriding labels * #563 override labels * #563 review fix --- atramhasis/templates/tree.jinja2 | 2 +- atramhasis/views/views.py | 10 ++++++--- tests/test_views.py | 38 +++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/atramhasis/templates/tree.jinja2 b/atramhasis/templates/tree.jinja2 index 80b17665..77a293e5 100644 --- a/atramhasis/templates/tree.jinja2 +++ b/atramhasis/templates/tree.jinja2 @@ -60,7 +60,7 @@ var current = '{{ conceptscheme.scheme_id }}'; {% endif %} d3.json('{{ request.route_path("scheme_tree", scheme_id = scheme_id, _query={'language':locale}) }}', - function (error, tree) { + function(error, tree) { if (error) throw error; var treeData = { 'label': scheme_label, 'scheme_id': scheme_id, children: tree } diff --git a/atramhasis/views/views.py b/atramhasis/views/views.py index ce0e57e1..a847d8b4 100644 --- a/atramhasis/views/views.py +++ b/atramhasis/views/views.py @@ -116,8 +116,12 @@ def conceptscheme_view(self): scheme_id = self.request.matchdict['scheme_id'] provider = self.request.skos_registry.get_provider(scheme_id) conceptscheme = provider.concept_scheme - title = conceptscheme.label(self.request.locale_name).label if (conceptscheme.label()) \ - else scheme_id + if 'atramhasis.force_display_label_language' in provider.metadata: + locale = provider.metadata['atramhasis.force_display_label_language'] + else: + locale = self.request.locale_name + title = (conceptscheme.label(locale).label if (conceptscheme.label()) + else scheme_id) scheme = { 'scheme_id': scheme_id, @@ -129,7 +133,7 @@ def conceptscheme_view(self): } return {'conceptscheme': scheme, 'conceptschemes': conceptschemes, - 'locale': self.request.locale_name} + 'locale': locale} @audit @view_config(route_name='concept', renderer='atramhasis:templates/concept.jinja2') diff --git a/tests/test_views.py b/tests/test_views.py index d9c70fa7..a6b966ea 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -2,18 +2,30 @@ import os import unittest -from skosprovider.registry import Registry +from paste.deploy.loadwsgi import appconfig from pyramid import testing -from skosprovider_sqlalchemy.models import Concept, Collection, Thing, Label, Note, LabelType, ConceptScheme +from skosprovider.registry import Registry +from skosprovider_sqlalchemy.models import Collection +from skosprovider_sqlalchemy.models import Concept +from skosprovider_sqlalchemy.models import ConceptScheme +from skosprovider_sqlalchemy.models import Label +from skosprovider_sqlalchemy.models import LabelType +from skosprovider_sqlalchemy.models import Note +from skosprovider_sqlalchemy.models import Thing from sqlalchemy.orm.exc import NoResultFound from webob.multidict import MultiDict -from paste.deploy.loadwsgi import appconfig -from atramhasis.cache import list_region -from atramhasis.data.datamanagers import SkosManager, ConceptSchemeManager, AuditManager -from atramhasis.errors import SkosRegistryNotFoundException, ConceptSchemeNotFoundException, ConceptNotFoundException -from atramhasis.views.views import AtramhasisView, AtramhasisAdminView, AtramhasisListView, \ - labels_to_string, get_definition +from atramhasis.data.datamanagers import AuditManager +from atramhasis.data.datamanagers import ConceptSchemeManager +from atramhasis.data.datamanagers import SkosManager +from atramhasis.errors import ConceptNotFoundException +from atramhasis.errors import ConceptSchemeNotFoundException +from atramhasis.errors import SkosRegistryNotFoundException +from atramhasis.views.views import AtramhasisAdminView +from atramhasis.views.views import AtramhasisListView +from atramhasis.views.views import AtramhasisView +from atramhasis.views.views import get_definition +from atramhasis.views.views import labels_to_string from fixtures.data import trees try: @@ -260,6 +272,16 @@ def test_conceptscheme_view(self): self.assertIsNotNone(res['conceptscheme']['notes']) self.assertIsNotNone(res['conceptscheme']['top_concepts']) + def test_conceptscheme_view_language(self): + self.request.matchdict['scheme_id'] = 'TREES' + self.request.skos_registry.providers['TREES'].metadata[ + 'atramhasis.force_display_label_language'] = 'nl' + + atramhasisview = AtramhasisView(self.request) + res = atramhasisview.conceptscheme_view() + self.assertIsNotNone(res) + self.assertEqual(res['locale'], 'nl') + class TestConceptView(unittest.TestCase): def setUp(self): From 8c39962cffe8e256c5418624fccc508cce7f790e Mon Sep 17 00:00:00 2001 From: Koen Van Daele Date: Mon, 20 Jul 2020 17:53:20 +0200 Subject: [PATCH 5/5] Fix typo. --- docs/source/customisation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/customisation.rst b/docs/source/customisation.rst index cffe8059..6d8018a5 100644 --- a/docs/source/customisation.rst +++ b/docs/source/customisation.rst @@ -299,7 +299,7 @@ maintain it from the admin interface. Force a display language for a vocabulary ========================================= -Under normal cicrumstances, Atramhasis tries to provide the most +Under normal circumstances, Atramhasis tries to provide the most appropriate label for a certain concept or collection, based on some default configuration and the preferences of the end-user. Every provider can be marked as having a certain `default language` (English if not set), but Atramhasis