From 9d463b17707110bbc76ce1beb4dbe9549cd8e9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20De=CC=81le=CC=80ze?= Date: Wed, 19 Jun 2019 12:16:36 +0200 Subject: [PATCH 1/4] translations: Translations in french and german MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BETTER Add translations support for french and german. Signed-off-by: Sébastien Délèze --- sonar/theme/templates/sonar/frontpage.html | 2 +- sonar/translations/.gitkeep | 0 sonar/translations/de/LC_MESSAGES/messages.po | 236 ++++++++++++++++++ sonar/translations/fr/LC_MESSAGES/messages.po | 134 +++++----- sonar/translations/messages.pot | 26 +- 5 files changed, 322 insertions(+), 76 deletions(-) delete mode 100644 sonar/translations/.gitkeep create mode 100644 sonar/translations/de/LC_MESSAGES/messages.po diff --git a/sonar/theme/templates/sonar/frontpage.html b/sonar/theme/templates/sonar/frontpage.html index c98b57c19..45bf28882 100755 --- a/sonar/theme/templates/sonar/frontpage.html +++ b/sonar/theme/templates/sonar/frontpage.html @@ -47,7 +47,7 @@
-
Software under development !
+
{{_('Software under development !')}}
{% endblock %} diff --git a/sonar/translations/.gitkeep b/sonar/translations/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/sonar/translations/de/LC_MESSAGES/messages.po b/sonar/translations/de/LC_MESSAGES/messages.po new file mode 100644 index 000000000..0739ac841 --- /dev/null +++ b/sonar/translations/de/LC_MESSAGES/messages.po @@ -0,0 +1,236 @@ +# German translations for sonar. +# Copyright (C) 2019 RERO +# This file is distributed under the same license as the sonar project. +# FIRST AUTHOR , 2019. +# +msgid "" +msgstr "" +"Project-Id-Version: sonar 0.0.1\n" +"Report-Msgid-Bugs-To: software@rero.ch\n" +"POT-Creation-Date: 2019-06-19 12:13+0200\n" +"PO-Revision-Date: 2019-06-13 10:45+0200\n" +"Last-Translator: FULL NAME \n" +"Language: de\n" +"Language-Team: de \n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.7.0\n" + +#: sonar/config.py:47 +msgid "French" +msgstr "Français" + +#: sonar/config.py:48 +msgid "German" +msgstr "Deutsch" + +#: sonar/config.py:68 sonar/config.py:72 +msgid "Swiss Open Access Repository" +msgstr "Swiss Open Access Repository" + +#: sonar/config.py:95 +msgid "Welcome to Swiss Open Access Repository!" +msgstr "Willkommen im Swiss Open Access Repository!" + +#: sonar/config.py:277 +msgid "institution" +msgstr "Institution" + +#: sonar/config.py:278 +msgid "language" +msgstr "Sprache" + +#: sonar/config.py:279 +msgid "author" +msgstr "Autor" + +#: sonar/config.py:280 +msgid "subject" +msgstr "Stichwort" + +#: sonar/config.py:289 +msgid "Best match" +msgstr "Beste Übereinstimmung" + +#: sonar/config.py:295 +msgid "Most recent" +msgstr "Neueste" + +#: sonar/theme/templates/sonar/401.html:13 +msgid "Unauthorized" +msgstr "Nicht autorisiert" + +#: sonar/theme/templates/sonar/401.html:14 +msgid "You need to be authenticated to view this page." +msgstr "Sie müssen authentifiziert sein, um diese Seite anzuzeigen." + +#: sonar/theme/templates/sonar/403.html:13 +msgid "Permission required" +msgstr "Genehmigung erforderlich" + +#: sonar/theme/templates/sonar/403.html:14 +msgid "You do not have sufficient permissions to view this page." +msgstr "Du hast nicht genügend Berechtigungen, um diese Seite anzuzeigen." + +#: sonar/theme/templates/sonar/404.html:13 +msgid "Page not found" +msgstr "Seite nicht gefunden" + +#: sonar/theme/templates/sonar/404.html:14 +msgid "The page you are looking for could not be found." +msgstr "Die gesuchte Seite konnte nicht gefunden werden." + +#: sonar/theme/templates/sonar/500.html:13 +msgid "Internal server error" +msgstr "Interner Serverfehler" + +#: sonar/theme/templates/sonar/500.html:15 +msgid "Error identifier" +msgstr "Fehleridentifikationsnummer" + +#: sonar/theme/templates/sonar/admin_header.html:72 +#: sonar/theme/templates/sonar/partial/dropdown_user.html:8 +msgid "Logout" +msgstr "Abmeldung" + +#: sonar/theme/templates/sonar/footer.html:32 +msgid "Help" +msgstr "Hilfe" + +#: sonar/theme/templates/sonar/footer.html:35 +msgid "About" +msgstr "Über uns" + +#: sonar/theme/templates/sonar/footer.html:38 +msgid "Contact" +msgstr "Kontakt" + +#: sonar/theme/templates/sonar/footer.html:43 +#, python-format +msgid "Powered by RERO" +msgstr "Angetrieben von RERO" + +#: sonar/theme/templates/sonar/frontpage.html:33 +msgid "Search publications, authors, projects, ..." +msgstr "Suche nach Publikationen, Autoren, Projekten, ..." + +#: sonar/theme/templates/sonar/frontpage.html:35 +msgid "Advanced search" +msgstr "Erweiterte Suche" + +#: sonar/theme/templates/sonar/frontpage.html:50 +msgid "Software under development !" +msgstr "Software in Entwicklung !" + +#: sonar/theme/templates/sonar/frontpage.html:57 +msgid "The project" +msgstr "Das Projekt" + +#: sonar/theme/templates/sonar/frontpage.html:58 +msgid "" +"The SONAR project aims to create a scholarly archive that collects, " +"promotes and preserves the publications of authors affiliated with Swiss " +"public research institutions." +msgstr "" +"Das SONAR-Projekt zielt darauf ab, ein wissenschaftliches Archiv zu " +"schaffen, das Folgendes sammelt, fördert und bewahrt die Publikationen " +"von mit der Schweiz verbundenen Autoren öffentliche " +"Forschungseinrichtungen." + +#: sonar/theme/templates/sonar/frontpage.html:59 +msgid "Further info on the project page" +msgstr "Weitere Informationen auf der Projektseite" + +#: sonar/theme/templates/sonar/frontpage.html:62 +msgid "Institution views (test)" +msgstr "Institutsansichten (Test)" + +#: sonar/theme/templates/sonar/frontpage.html:66 +msgid "Follow us" +msgstr "Folgen Sie uns" + +#: sonar/theme/templates/sonar/frontpage.html:71 +msgid "Project website on" +msgstr "Projekt-Website auf" + +#: sonar/theme/templates/sonar/frontpage.html:77 +msgid "Source code on" +msgstr "Sourcecode auf github" + +#: sonar/theme/templates/sonar/page.html:34 +#: sonar/theme/templates/sonar/page_admin.html:34 +msgid "Invenio" +msgstr "Invenio" + +#: sonar/theme/templates/sonar/page_settings.html:19 +msgid "Settings" +msgstr "Einstellungen" + +#: sonar/theme/templates/sonar/search.html:45 +msgid "Sort by" +msgstr "Sortiert nach" + +#: sonar/theme/templates/sonar/search.html:80 +msgid "Loading..." +msgstr "Ladung..." + +#: sonar/theme/templates/sonar/search.html:87 +msgid "Search failed." +msgstr "Suche fehlgeschlagen." + +#: sonar/theme/templates/sonar/accounts/forgot_password.html:11 +#: sonar/theme/templates/sonar/accounts/forgot_password.html:21 +msgid "Reset Password" +msgstr "Passwort zurücksetzen" + +#: sonar/theme/templates/sonar/accounts/forgot_password.html:17 +msgid "" +"Enter your email address below and we will send you a link to reset your " +"password." +msgstr "" +"Geben Sie unten Ihre E-Mail-Adresse ein und wir senden Ihnen einen Link, " +"um Ihr Passwort zurückzusetzen." + +#: sonar/theme/templates/sonar/accounts/forgot_password.html:28 +#: sonar/theme/templates/sonar/accounts/login.html:16 +#: sonar/theme/templates/sonar/accounts/signup.html:31 +msgid "Log In" +msgstr "Einloggen" + +#: sonar/theme/templates/sonar/accounts/forgot_password.html:30 +#: sonar/theme/templates/sonar/accounts/login.html:24 +#: sonar/theme/templates/sonar/accounts/signup.html:25 +msgid "Sign Up" +msgstr "Registrieren" + +#: sonar/theme/templates/sonar/accounts/login.html:9 +msgid "Log in to account" +msgstr "Melden Sie sich bei Ihrem Konto an" + +#: sonar/theme/templates/sonar/accounts/login.html:22 +msgid "Forgot password?" +msgstr "Passwort vergessen?" + +#: sonar/theme/templates/sonar/accounts/signup.html:10 +#, python-format +msgid "Sign up for a %(sitename)s account!" +msgstr "Melde dich für ein %(sitename)-Konto an!" + +#: sonar/theme/templates/sonar/partial/dropdown_user.html:5 +msgid "Profile" +msgstr "Profil" + +#: sonar/theme/templates/sonar/partial/navbar.html:26 +msgid "Search" +msgstr "Suche" + +#: sonar/theme/templates/sonar/partial/navbar.html:36 +msgid "Back to SONAR" +msgstr "Zurück zu SONAR" + +#: sonar/theme/templates/sonar/partial/navbar.html:44 +msgid "Log in" +msgstr "Einloggen" + diff --git a/sonar/translations/fr/LC_MESSAGES/messages.po b/sonar/translations/fr/LC_MESSAGES/messages.po index 0abbe71e0..3225924ec 100644 --- a/sonar/translations/fr/LC_MESSAGES/messages.po +++ b/sonar/translations/fr/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: sonar 0.0.1\n" "Report-Msgid-Bugs-To: software@rero.ch\n" -"POT-Creation-Date: 2019-06-06 12:05+0200\n" +"POT-Creation-Date: 2019-06-19 12:13+0200\n" "PO-Revision-Date: 2019-06-06 11:09+0200\n" "Last-Translator: FULL NAME \n" "Language: fr\n" @@ -20,19 +20,35 @@ msgstr "" #: sonar/config.py:47 msgid "French" -msgstr "" +msgstr "Français" #: sonar/config.py:48 msgid "German" -msgstr "" +msgstr "Deutsch" #: sonar/config.py:68 sonar/config.py:72 msgid "Swiss Open Access Repository" -msgstr "" +msgstr "Swiss Open Access Repository" #: sonar/config.py:95 msgid "Welcome to Swiss Open Access Repository!" -msgstr "" +msgstr "Bienvenue sur Swiss Open Access Repository" + +#: sonar/config.py:277 +msgid "institution" +msgstr "Institution" + +#: sonar/config.py:278 +msgid "language" +msgstr "Langue" + +#: sonar/config.py:279 +msgid "author" +msgstr "Author" + +#: sonar/config.py:280 +msgid "subject" +msgstr "Mot-clé" #: sonar/config.py:289 msgid "Best match" @@ -44,19 +60,19 @@ msgstr "Les plus récents" #: sonar/theme/templates/sonar/401.html:13 msgid "Unauthorized" -msgstr "" +msgstr "Non autorisé" #: sonar/theme/templates/sonar/401.html:14 msgid "You need to be authenticated to view this page." -msgstr "" +msgstr "Vous devez être authentifié pour voir cette page." #: sonar/theme/templates/sonar/403.html:13 msgid "Permission required" -msgstr "" +msgstr "Permission requise" #: sonar/theme/templates/sonar/403.html:14 msgid "You do not have sufficient permissions to view this page." -msgstr "" +msgstr "Vous n'avez pas suffisamment de permissions pour voir cette page." #: sonar/theme/templates/sonar/404.html:13 msgid "Page not found" @@ -64,53 +80,53 @@ msgstr "Page non trouvée" #: sonar/theme/templates/sonar/404.html:14 msgid "The page you are looking for could not be found." -msgstr "" +msgstr "La page que vous recherchez n'a pas pu être trouvée." #: sonar/theme/templates/sonar/500.html:13 msgid "Internal server error" -msgstr "" +msgstr "Erreur interne du serveur" #: sonar/theme/templates/sonar/500.html:15 msgid "Error identifier" -msgstr "" +msgstr "Identifiant d'erreur" #: sonar/theme/templates/sonar/admin_header.html:72 #: sonar/theme/templates/sonar/partial/dropdown_user.html:8 msgid "Logout" -msgstr "" - -#: sonar/theme/templates/sonar/footer.html:19 -msgid "Language:" -msgstr "" +msgstr "Déconnexion" #: sonar/theme/templates/sonar/footer.html:32 msgid "Help" -msgstr "" +msgstr "Aide" #: sonar/theme/templates/sonar/footer.html:35 msgid "About" -msgstr "" +msgstr "A propos" #: sonar/theme/templates/sonar/footer.html:38 msgid "Contact" -msgstr "" +msgstr "Contact" #: sonar/theme/templates/sonar/footer.html:43 #, python-format msgid "Powered by RERO" -msgstr "" +msgstr "Réalisé par RERO" #: sonar/theme/templates/sonar/frontpage.html:33 msgid "Search publications, authors, projects, ..." -msgstr "" +msgstr "Recherche de publications, auteurs, projets, ..." #: sonar/theme/templates/sonar/frontpage.html:35 msgid "Advanced search" -msgstr "" +msgstr "Recherche avancée" + +#: sonar/theme/templates/sonar/frontpage.html:50 +msgid "Software under development !" +msgstr "Logiciel en cours de développement" #: sonar/theme/templates/sonar/frontpage.html:57 msgid "The project" -msgstr "" +msgstr "Le projet" #: sonar/theme/templates/sonar/frontpage.html:58 msgid "" @@ -118,35 +134,38 @@ msgid "" "promotes and preserves the publications of authors affiliated with Swiss " "public research institutions." msgstr "" +"Le projet SONAR a pour but de créer une archive scientifique qui " +"recueille, promeut et préserve les publications des auteurs affiliés aux " +"institutions publiques suisses de recherche." #: sonar/theme/templates/sonar/frontpage.html:59 msgid "Further info on the project page" -msgstr "" +msgstr "Plus d'informations sur la page du projet" #: sonar/theme/templates/sonar/frontpage.html:62 msgid "Institution views (test)" -msgstr "" +msgstr "Vues institutionelles (test)" #: sonar/theme/templates/sonar/frontpage.html:66 msgid "Follow us" -msgstr "" +msgstr "Suivez-nous" #: sonar/theme/templates/sonar/frontpage.html:71 msgid "Project website on" -msgstr "" +msgstr "Site web du projet sur" #: sonar/theme/templates/sonar/frontpage.html:77 msgid "Source code on" -msgstr "" +msgstr "Code source sur" #: sonar/theme/templates/sonar/page.html:34 #: sonar/theme/templates/sonar/page_admin.html:34 msgid "Invenio" -msgstr "" +msgstr "Invenio" #: sonar/theme/templates/sonar/page_settings.html:19 msgid "Settings" -msgstr "" +msgstr "Réglages" #: sonar/theme/templates/sonar/search.html:45 msgid "Sort by" @@ -154,88 +173,63 @@ msgstr "Trier par" #: sonar/theme/templates/sonar/search.html:80 msgid "Loading..." -msgstr "" +msgstr "Chargement..." #: sonar/theme/templates/sonar/search.html:87 msgid "Search failed." -msgstr "" +msgstr "Echec de la recherche." #: sonar/theme/templates/sonar/accounts/forgot_password.html:11 #: sonar/theme/templates/sonar/accounts/forgot_password.html:21 msgid "Reset Password" -msgstr "" +msgstr "Réinitialiser le mot de passe" #: sonar/theme/templates/sonar/accounts/forgot_password.html:17 msgid "" "Enter your email address below and we will send you a link to reset your " "password." msgstr "" +"Entrez votre adresse email ci-dessous et nous vous enverrons un lien pour" +" réinitialiser votre mot de passe." #: sonar/theme/templates/sonar/accounts/forgot_password.html:28 #: sonar/theme/templates/sonar/accounts/login.html:16 #: sonar/theme/templates/sonar/accounts/signup.html:31 msgid "Log In" -msgstr "" +msgstr "Identification" #: sonar/theme/templates/sonar/accounts/forgot_password.html:30 #: sonar/theme/templates/sonar/accounts/login.html:24 #: sonar/theme/templates/sonar/accounts/signup.html:25 msgid "Sign Up" -msgstr "" +msgstr "S'inscrire" #: sonar/theme/templates/sonar/accounts/login.html:9 msgid "Log in to account" -msgstr "" +msgstr "Connectez-vous à votre compte" #: sonar/theme/templates/sonar/accounts/login.html:22 msgid "Forgot password?" -msgstr "" +msgstr "Mot de passe oublié?" #: sonar/theme/templates/sonar/accounts/signup.html:10 #, python-format msgid "Sign up for a %(sitename)s account!" -msgstr "" +msgstr "Inscrivez-vous pour un compte %(sitename)!" #: sonar/theme/templates/sonar/partial/dropdown_user.html:5 msgid "Profile" -msgstr "" +msgstr "Profile" #: sonar/theme/templates/sonar/partial/navbar.html:26 msgid "Search" -msgstr "" +msgstr "Recherche" #: sonar/theme/templates/sonar/partial/navbar.html:36 msgid "Back to SONAR" -msgstr "" +msgstr "Retour à SONAR" #: sonar/theme/templates/sonar/partial/navbar.html:44 msgid "Log in" -msgstr "" - -#~ msgid "institution" -#~ msgstr "" - -#~ msgid "language" -#~ msgstr "" - -#~ msgid "author" -#~ msgstr "Auteur" - -#~ msgid "subject" -#~ msgstr "" - -#~ msgid "Language:" -#~ msgstr "" - -#~ msgid "Institution" -#~ msgstr "Institution" - -#~ msgid "Language" -#~ msgstr "Langue" - -#~ msgid "Author" -#~ msgstr "Auteur" - -#~ msgid "Subject" -#~ msgstr "Sujet" +msgstr "Identification" diff --git a/sonar/translations/messages.pot b/sonar/translations/messages.pot index eeccd083d..ca02049bb 100644 --- a/sonar/translations/messages.pot +++ b/sonar/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: sonar 0.0.1\n" "Report-Msgid-Bugs-To: software@rero.ch\n" -"POT-Creation-Date: 2019-06-06 12:05+0200\n" +"POT-Creation-Date: 2019-06-19 12:13+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -33,6 +33,22 @@ msgstr "" msgid "Welcome to Swiss Open Access Repository!" msgstr "" +#: sonar/config.py:277 +msgid "institution" +msgstr "" + +#: sonar/config.py:278 +msgid "language" +msgstr "" + +#: sonar/config.py:279 +msgid "author" +msgstr "" + +#: sonar/config.py:280 +msgid "subject" +msgstr "" + #: sonar/config.py:289 msgid "Best match" msgstr "" @@ -78,10 +94,6 @@ msgstr "" msgid "Logout" msgstr "" -#: sonar/theme/templates/sonar/footer.html:19 -msgid "Language:" -msgstr "" - #: sonar/theme/templates/sonar/footer.html:32 msgid "Help" msgstr "" @@ -107,6 +119,10 @@ msgstr "" msgid "Advanced search" msgstr "" +#: sonar/theme/templates/sonar/frontpage.html:50 +msgid "Software under development !" +msgstr "" + #: sonar/theme/templates/sonar/frontpage.html:57 msgid "The project" msgstr "" From 2bf25eecf1463486cabaa28067ba89e0fae2182f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20De=CC=81le=CC=80ze?= Date: Wed, 19 Jun 2019 15:24:38 +0200 Subject: [PATCH 2/4] templating: Document detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NEW Format and show information on document detail page. BETTER Add black support for code formatting Signed-off-by: Sébastien Délèze --- .gitignore | 1 + Pipfile | 5 +- Pipfile.lock | 229 ++++++++------- pytest.ini | 4 +- run-tests.sh | 2 +- sonar/config.py | 10 +- sonar/modules/config.py | 2 +- sonar/modules/documents/cli.py | 61 ++-- .../dojson/contrib/marc21tojson/__init__.py | 2 +- .../dojson/contrib/marc21tojson/model.py | 277 +++++++++--------- .../documents/document-v1.0.0.json | 74 +++-- sonar/modules/documents/loaders/__init__.py | 10 +- .../v6/documents/document-v1.0.0.json | 45 ++- sonar/modules/documents/marshmallow/json.py | 12 +- .../modules/documents/serializers/__init__.py | 16 +- .../documents/search_ui/results.html | 10 +- .../documents/templates/documents/record.html | 106 +++++-- sonar/modules/documents/views.py | 125 ++++++-- .../modules/institutions/loaders/__init__.py | 10 +- .../modules/institutions/marshmallow/json.py | 7 +- .../institutions/serializers/__init__.py | 16 +- sonar/theme/templates/sonar/macros/macro.html | 8 + sonar/translations/de/LC_MESSAGES/messages.po | 51 ++-- sonar/translations/fr/LC_MESSAGES/messages.po | 51 ++-- sonar/translations/messages.pot | 42 ++- tests/api/test_api_simple_flow.py | 14 +- tests/e2e/test_front_page.py | 4 +- tests/ui/documents/test_documents_cli.py | 40 ++- tests/ui/documents/test_documents_views.py | 51 +++- tests/ui/documents/test_marc21tojson.py | 190 ++++++------ .../ui/institutions/test_institutions_cli.py | 13 +- 31 files changed, 874 insertions(+), 614 deletions(-) create mode 100644 sonar/theme/templates/sonar/macros/macro.html diff --git a/.gitignore b/.gitignore index 2cb107429..c74cf8055 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ htmlcov/ nosetests.xml coverage.xml *,cover +.pytest_cache # end-to-end testing .e2e_screenshots diff --git a/Pipfile b/Pipfile index a7d5dc986..32ae7891f 100644 --- a/Pipfile +++ b/Pipfile @@ -6,13 +6,14 @@ name = "pypi" [packages] Babel = ">=2.4.0" Flask-BabelEx = ">=0.9.3" -invenio = { version = "==3.1.0", extras = ["base", "metadata", "postgresql", "auth", "elasticsearch6" ]} +invenio = {version = "==3.1.0",extras = ["base", "metadata", "postgresql", "auth", "elasticsearch6" ]} # TODO: remove this contraint once it is solved in invenio -raven = { version=">=6.0", extras=["flask"] } +raven = {version = ">=6.0",extras = ["flask"]} uwsgi = ">=2.0" uwsgitop = ">=0.11" uwsgi-tools = ">=1.1.1" lxml = ">=3.5.0,<4.2.6" +pycountry = "*" [dev-packages] Flask-Debugtoolbar = ">=0.10.1" diff --git a/Pipfile.lock b/Pipfile.lock index 3802d93f5..e55e77f6e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "6b511811dbd7063df6a6ee5ee368bf6ce4fe3ece83474773b82572a6468b5df5" + "sha256": "bfcb953244e29ca5cb45ef55285b95c68ccad396df53c96c6846bf6f2601d527" }, "pipfile-spec": 6, "requires": { @@ -24,10 +24,10 @@ }, "amqp": { "hashes": [ - "sha256:043beb485774ca69718a35602089e524f87168268f0d1ae115f28b88d27f92d7", - "sha256:35a3b5006ca00b21aaeec8ceea07130f07b902dd61bfe42815039835f962f5f1" + "sha256:aa4409446139676943a2eaa27d5f58caf750f4ca5a89f888c452afd86be6a67d", + "sha256:cbb6f87d53cac612a594f982b717cc1c54c6a1e17943a0a0d32dc6cc9e2120c8" ], - "version": "==2.4.2" + "version": "==2.5.0" }, "angular-gettext-babel": { "hashes": [ @@ -45,10 +45,10 @@ }, "arrow": { "hashes": [ - "sha256:002f2315cf4c8404de737c42860441732d339bbc57fee584e2027520e055ecc1", - "sha256:82dd5e13b733787d4eb0fef42d1ee1a99136dc1d65178f70373b3678b3181bfc" + "sha256:03404b624e89ac5e4fc19c52045fa0f3203419fd4dd64f6e8958c522580a574a", + "sha256:41be7ea4c53c2cf57bf30f2d614f60c411160133f7a0a8c49111c30fb7e725b5" ], - "version": "==0.13.2" + "version": "==0.14.2" }, "asn1crypto": { "hashes": [ @@ -114,10 +114,10 @@ }, "certifi": { "hashes": [ - "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", - "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" ], - "version": "==2019.3.9" + "version": "==2019.6.16" }, "cffi": { "hashes": [ @@ -175,27 +175,24 @@ }, "cryptography": { "hashes": [ - "sha256:066f815f1fe46020877c5983a7e747ae140f517f1b09030ec098503575265ce1", - "sha256:210210d9df0afba9e000636e97810117dc55b7157c903a55716bb73e3ae07705", - "sha256:26c821cbeb683facb966045e2064303029d572a87ee69ca5a1bf54bf55f93ca6", - "sha256:2afb83308dc5c5255149ff7d3fb9964f7c9ee3d59b603ec18ccf5b0a8852e2b1", - "sha256:2db34e5c45988f36f7a08a7ab2b69638994a8923853dec2d4af121f689c66dc8", - "sha256:409c4653e0f719fa78febcb71ac417076ae5e20160aec7270c91d009837b9151", - "sha256:45a4f4cf4f4e6a55c8128f8b76b4c057027b27d4c67e3fe157fa02f27e37830d", - "sha256:48eab46ef38faf1031e58dfcc9c3e71756a1108f4c9c966150b605d4a1a7f659", - "sha256:6b9e0ae298ab20d371fc26e2129fd683cfc0cfde4d157c6341722de645146537", - "sha256:6c4778afe50f413707f604828c1ad1ff81fadf6c110cb669579dea7e2e98a75e", - "sha256:8c33fb99025d353c9520141f8bc989c2134a1f76bac6369cea060812f5b5c2bb", - "sha256:9873a1760a274b620a135054b756f9f218fa61ca030e42df31b409f0fb738b6c", - "sha256:9b069768c627f3f5623b1cbd3248c5e7e92aec62f4c98827059eed7053138cc9", - "sha256:9e4ce27a507e4886efbd3c32d120db5089b906979a4debf1d5939ec01b9dd6c5", - "sha256:acb424eaca214cb08735f1a744eceb97d014de6530c1ea23beb86d9c6f13c2ad", - "sha256:c8181c7d77388fe26ab8418bb088b1a1ef5fde058c6926790c8a0a3d94075a4a", - "sha256:d4afbb0840f489b60f5a580a41a1b9c3622e08ecb5eec8614d4fb4cd914c4460", - "sha256:d9ed28030797c00f4bc43c86bf819266c76a5ea61d006cd4078a93ebf7da6bfd", - "sha256:e603aa7bb52e4e8ed4119a58a03b60323918467ef209e6ff9db3ac382e5cf2c6" - ], - "version": "==2.6.1" + "sha256:24b61e5fcb506424d3ec4e18bca995833839bf13c59fc43e530e488f28d46b8c", + "sha256:25dd1581a183e9e7a806fe0543f485103232f940fcfc301db65e630512cce643", + "sha256:3452bba7c21c69f2df772762be0066c7ed5dc65df494a1d53a58b683a83e1216", + "sha256:41a0be220dd1ed9e998f5891948306eb8c812b512dc398e5a01846d855050799", + "sha256:5751d8a11b956fbfa314f6553d186b94aa70fdb03d8a4d4f1c82dcacf0cbe28a", + "sha256:5f61c7d749048fa6e3322258b4263463bfccefecb0dd731b6561cb617a1d9bb9", + "sha256:72e24c521fa2106f19623a3851e9f89ddfdeb9ac63871c7643790f872a305dfc", + "sha256:7b97ae6ef5cba2e3bb14256625423413d5ce8d1abb91d4f29b6d1a081da765f8", + "sha256:961e886d8a3590fd2c723cf07be14e2a91cf53c25f02435c04d39e90780e3b53", + "sha256:96d8473848e984184b6728e2c9d391482008646276c3ff084a1bd89e15ff53a1", + "sha256:ae536da50c7ad1e002c3eee101871d93abdc90d9c5f651818450a0d3af718609", + "sha256:b0db0cecf396033abb4a93c95d1602f268b3a68bb0a9cc06a7cff587bb9a7292", + "sha256:cfee9164954c186b191b91d4193989ca994703b2fff406f71cf454a2d3c7327e", + "sha256:e6347742ac8f35ded4a46ff835c60e68c22a536a8ae5c4422966d06946b6d4c6", + "sha256:f27d93f0139a3c056172ebb5d4f9056e770fdf0206c2f422ff2ebbad142e09ed", + "sha256:f57b76e46a58b63d1c6375017f4564a28f19a5ca912691fd2e4261b3414b618d" + ], + "version": "==2.7" }, "decorator": { "hashes": [ @@ -288,10 +285,10 @@ }, "flask-cors": { "hashes": [ - "sha256:7ad56ee3b90d4955148fc25a2ecaa1124fc84298471e266a7fea59aeac4405a5", - "sha256:7e90bf225fdf163d11b84b59fb17594d0580a16b97ab4e1146b1fb2737c1cfec" + "sha256:72170423eb4612f0847318afff8c247b38bd516b7737adfc10d1c2cdbb382d16", + "sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a" ], - "version": "==3.0.7" + "version": "==3.0.8" }, "flask-kvsession": { "hashes": [ @@ -407,10 +404,10 @@ }, "importlib-metadata": { "hashes": [ - "sha256:027cfc6524613de726789072f95d2e4cc64dd1dee8096d42d13f2ead5bd302f5", - "sha256:0d05199e1f0b1a8707a1b9c46476d4a49807fb56cb1b0737db1d37feb42fe31d" + "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", + "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db" ], - "version": "==0.15" + "version": "==0.18" }, "infinity": { "hashes": [ @@ -529,9 +526,9 @@ }, "invenio-indexer": { "hashes": [ - "sha256:f51a7eda14048da000b4a1ee74a137fd33d9b96119daa968af1b2fe6a4421e8c" + "sha256:ca8c705813eb1256625f3c6d1550a0610be693676d4929f2f63e0fcf17879973" ], - "version": "==1.0.1" + "version": "==1.0.2" }, "invenio-jsonschemas": { "hashes": [ @@ -542,10 +539,10 @@ }, "invenio-logging": { "hashes": [ - "sha256:1987d2ce7bb17c910b1e67cc553e191bf5b39c18c4cc118f408bf35ad23f2fd1", - "sha256:bf750a67a71c5d9b30680127baf4c25765fccc6df4dc9a2114ca94eef5672cb0" + "sha256:8c2f6633905d100867814e5894564e79682bca7e45ea2870a6c03a69c4860dec", + "sha256:f3a87ab70993e6c7eb5990e70dda3ab0729b1bfa6c90ecf1f9890018accb926f" ], - "version": "==1.1.0" + "version": "==1.1.1" }, "invenio-mail": { "hashes": [ @@ -671,10 +668,10 @@ }, "jedi": { "hashes": [ - "sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b", - "sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c" + "sha256:49ccb782651bb6f7009810d17a3316f8867dde31654c750506970742e18b553d", + "sha256:79d0f6595f3846dffcbe667cc6dc821b96e5baa8add125176c31a3917eb19d58" ], - "version": "==0.13.3" + "version": "==0.14.0" }, "jinja2": { "hashes": [ @@ -733,10 +730,10 @@ }, "kombu": { "hashes": [ - "sha256:389ba09e03b15b55b1a7371a441c894fd8121d174f5583bbbca032b9ea8c9edd", - "sha256:7b92303af381ef02fad6899fd5f5a9a96031d781356cd8e505fa54ae5ddee181" + "sha256:55b71d3785def3470a16217fe0780f9e6f95e61bf9ad39ef8dce0177224eab77", + "sha256:eb365ea795cd7e629ba2f1f398e0c3ba354b91ef4de225ffdf6ab45fdfc7d581" ], - "version": "==4.5.0" + "version": "==4.6.3" }, "limits": { "hashes": [ @@ -783,9 +780,9 @@ }, "mako": { "hashes": [ - "sha256:7165919e78e1feb68b4dbe829871ea9941398178fa58e6beedb9ba14acf63965" + "sha256:0cfa65de3a835e87eeca6ac856b3013aade55f49e32515f65d999f91a2324162" ], - "version": "==1.0.10" + "version": "==1.0.12" }, "markupsafe": { "hashes": [ @@ -822,10 +819,10 @@ }, "marshmallow": { "hashes": [ - "sha256:0e497a6447ffaad55578138ca512752de7a48d12f444996ededc3d6bf8a09ca2", - "sha256:e21a4dea20deb167c723e0ffb13f4cf33bcbbeb8a334e92406a3308cedea2826" + "sha256:9cedfc5b6f568d57e8a2cf3d293fbd81b05e5ef557854008d03e25660a39ccfd", + "sha256:a4d99922116a76e5abd8f997ec0519086e24814b7e1e1344bebe2a312ba50235" ], - "version": "==2.19.2" + "version": "==2.19.5" }, "maxminddb": { "hashes": [ @@ -860,10 +857,10 @@ }, "parso": { "hashes": [ - "sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33", - "sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376" + "sha256:5052bb33be034cba784193e74b1cde6ebf29ae8b8c1e4ad94df0c4209bfc4826", + "sha256:db5881df1643bf3e66c097bfd8935cf03eae73f4cb61ae4433c9ea4fb6613446" ], - "version": "==0.4.0" + "version": "==0.5.0" }, "passlib": { "hashes": [ @@ -911,36 +908,36 @@ }, "psycopg2-binary": { "hashes": [ - "sha256:007ca0df127b1862fc010125bc4100b7a630efc6841047bd11afceadb4754611", - "sha256:03c49e02adf0b4d68f422fdbd98f7a7c547beb27e99a75ed02298f85cb48406a", - "sha256:0a1232cdd314e08848825edda06600455ad2a7adaa463ebfb12ece2d09f3370e", - "sha256:131c80d0958c89273d9720b9adf9df1d7600bb3120e16019a7389ab15b079af5", - "sha256:2de34cc3b775724623f86617d2601308083176a495f5b2efc2bbb0da154f483a", - "sha256:2eddc31500f73544a2a54123d4c4b249c3c711d31e64deddb0890982ea37397a", - "sha256:484f6c62bdc166ee0e5be3aa831120423bf399786d1f3b0304526c86180fbc0b", - "sha256:4c2d9369ed40b4a44a8ccd6bc3a7db6272b8314812d2d1091f95c4c836d92e06", - "sha256:70f570b5fa44413b9f30dbc053d17ef3ce6a4100147a10822f8662e58d473656", - "sha256:7a2b5b095f3bd733aab101c89c0e1a3f0dfb4ebdc26f6374805c086ffe29d5b2", - "sha256:804914a669186e2843c1f7fbe12b55aad1b36d40a28274abe6027deffad9433d", - "sha256:8520c03172da18345d012949a53617a963e0191ccb3c666f23276d5326af27b5", - "sha256:90da901fc33ea393fc644607e4a3916b509387e9339ec6ebc7bfded45b7a0ae9", - "sha256:a582416ad123291a82c300d1d872bdc4136d69ad0b41d57dc5ca3df7ef8e3088", - "sha256:ac8c5e20309f4989c296d62cac20ee456b69c41fd1bc03829e27de23b6fa9dd0", - "sha256:b2cf82f55a619879f8557fdaae5cec7a294fac815e0087c4f67026fdf5259844", - "sha256:b59d6f8cfca2983d8fdbe457bf95d2192f7b7efdb2b483bf5fa4e8981b04e8b2", - "sha256:be08168197021d669b9964bd87628fa88f910b1be31e7010901070f2540c05fd", - "sha256:be0f952f1c365061041bad16e27e224e29615d4eb1fb5b7e7760a1d3d12b90b6", - "sha256:c1c9a33e46d7c12b9c96cf2d4349d783e3127163fd96254dcd44663cf0a1d438", - "sha256:d18c89957ac57dd2a2724ecfe9a759912d776f96ecabba23acb9ecbf5c731035", - "sha256:d7e7b0ff21f39433c50397e60bf0995d078802c591ca3b8d99857ea18a7496ee", - "sha256:da0929b2bf0d1f365345e5eb940d8713c1d516312e010135b14402e2a3d2404d", - "sha256:de24a4962e361c512d3e528ded6c7480eab24c655b8ca1f0b761d3b3650d2f07", - "sha256:e45f93ff3f7dae2202248cf413a87aeb330821bf76998b3cf374eda2fc893dd7", - "sha256:f046aeae1f7a845041b8661bb7a52449202b6c5d3fb59eb4724e7ca088811904", - "sha256:f1dc2b7b2748084b890f5d05b65a47cd03188824890e9a60818721fd492249fb", - "sha256:fcbe7cf3a786572b73d2cd5f34ed452a5f5fac47c9c9d1e0642c457a148f9f88" - ], - "version": "==2.8.2" + "sha256:080c72714784989474f97be9ab0ddf7b2ad2984527e77f2909fcd04d4df53809", + "sha256:110457be80b63ff4915febb06faa7be002b93a76e5ba19bf3f27636a2ef58598", + "sha256:171352a03b22fc099f15103959b52ee77d9a27e028895d7e5fde127aa8e3bac5", + "sha256:19d013e7b0817087517a4b3cab39c084d78898369e5c46258aab7be4f233d6a1", + "sha256:249b6b21ae4eb0f7b8423b330aa80fab5f821b9ffc3f7561a5e2fd6bb142cf5d", + "sha256:2ac0731d2d84b05c7bb39e85b7e123c3a0acd4cda631d8d542802c88deb9e87e", + "sha256:2b6d561193f0dc3f50acfb22dd52ea8c8dfbc64bcafe3938b5f209cc17cb6f00", + "sha256:2bd23e242e954214944481124755cbefe7c2cf563b1a54cd8d196d502f2578bf", + "sha256:3e1239242ca60b3725e65ab2f13765fc199b03af9eaf1b5572f0e97bdcee5b43", + "sha256:3eb70bb697abbe86b1d2b1316370c02ba320bfd1e9e35cf3b9566a855ea8e4e5", + "sha256:51a2fc7e94b98bd1bb5d4570936f24fc2b0541b63eccadf8fdea266db8ad2f70", + "sha256:52f1bdafdc764b7447e393ed39bb263eccb12bfda25a4ac06d82e3a9056251f6", + "sha256:5b3581319a3951f1e866f4f6c5e42023db0fae0284273b82e97dfd32c51985cd", + "sha256:63c1b66e3b2a3a336288e4bcec499e0dc310cd1dceaed1c46fa7419764c68877", + "sha256:8123a99f24ecee469e5c1339427bcdb2a33920a18bb5c0d58b7c13f3b0298ba3", + "sha256:85e699fcabe7f817c0f0a412d4e7c6627e00c412b418da7666ff353f38e30f67", + "sha256:8dbff4557bbef963697583366400822387cccf794ccb001f1f2307ed21854c68", + "sha256:908d21d08d6b81f1b7e056bbf40b2f77f8c499ab29e64ec5113052819ef1c89b", + "sha256:af39d0237b17d0a5a5f638e9dffb34013ce2b1d41441fd30283e42b22d16858a", + "sha256:af51bb9f055a3f4af0187149a8f60c9d516cf7d5565b3dac53358796a8fb2a5b", + "sha256:b2ecac57eb49e461e86c092761e6b8e1fd9654dbaaddf71a076dcc869f7014e2", + "sha256:cd37cc170678a4609becb26b53a2bc1edea65177be70c48dd7b39a1149cabd6e", + "sha256:d17e3054b17e1a6cb8c1140f76310f6ede811e75b7a9d461922d2c72973f583e", + "sha256:d305313c5a9695f40c46294d4315ed3a07c7d2b55e48a9010dad7db7a66c8b7f", + "sha256:dd0ef0eb1f7dd18a3f4187226e226a7284bda6af5671937a221766e6ef1ee88f", + "sha256:e1adff53b56db9905db48a972fb89370ad5736e0450b96f91bcf99cadd96cfd7", + "sha256:f0d43828003c82dbc9269de87aa449e9896077a71954fbbb10a614c017e65737", + "sha256:f78e8b487de4d92640105c1389e5b90be3496b1d75c90a666edd8737cc2dbab7" + ], + "version": "==2.8.3" }, "ptyprocess": { "hashes": [ @@ -949,6 +946,14 @@ ], "version": "==0.6.0" }, + "pycountry": { + "hashes": [ + "sha256:104a8ca94c700898c42a0172da2eab5a5675c49637b729a11db9e1dac2d983cd", + "sha256:8ec4020b2b15cd410893d573820d42ee12fe50365332e58c0975c953b60a16de" + ], + "index": "pypi", + "version": "==18.12.8" + }, "pycparser": { "hashes": [ "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" @@ -1084,9 +1089,9 @@ }, "sqlalchemy": { "hashes": [ - "sha256:c7fef198b43ef31dfd783d094fd5ee435ce8717592e6784c45ba337254998017" + "sha256:c30925d60af95443458ebd7525daf791f55762b106049ae71e18f8dd58084c2f" ], - "version": "==1.3.4" + "version": "==1.3.5" }, "sqlalchemy-continuum": { "hashes": [ @@ -1099,9 +1104,9 @@ "encrypted" ], "hashes": [ - "sha256:3f1cb542cf0549a0de508d4919f3ad693a36230bf4cd13fdd6253549fec71182" + "sha256:0ebd4d176a5786233db9f2e92040476fcff8b1b426fdbbb7ee4f478280ee9166" ], - "version": "==0.33.11" + "version": "==0.34.0" }, "traitlets": { "hashes": [ @@ -1175,11 +1180,11 @@ }, "webargs": { "hashes": [ - "sha256:34ccbd72bf76fd5779e1092b75497bf0b03ca19e6c1784a4f49e2b2bf2e6133a", - "sha256:3d707434a73abe1fca231935c41e6743eae16fc7888a4bfd666145af1554464d", - "sha256:888f3e7e0b74b760732388da4e73640fcab452045d78268c31fb610b3cb397b9" + "sha256:6b81ce44572d4f345104aa41c734fdc01165f054a061a8ebb1b46e89851e1170", + "sha256:713bd63440ee078ce48ca953d254d51e5f1a6fa0c76fb521fc596306c78d95a5", + "sha256:e2394ea7e422c1e795681cee5e8b1c6083bab7db6d7a380841130cbbae173d29" ], - "version": "==5.3.1" + "version": "==5.3.2" }, "webassets": { "hashes": [ @@ -1280,10 +1285,10 @@ }, "certifi": { "hashes": [ - "sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", - "sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae" + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" ], - "version": "==2019.3.9" + "version": "==2019.6.16" }, "chardet": { "hashes": [ @@ -1294,11 +1299,11 @@ }, "check-manifest": { "hashes": [ - "sha256:d3ef229a2629024f082922faa2173ceaf19390b7aa3c63bf0eea000dbee8a6dc", - "sha256:dba4749e3874d8f83eb4f0814c9c331fcade6c6325f885deb7b14aa1487fb323" + "sha256:8754cc8efd7c062a3705b442d1c23ff702d4477b41a269c2e354b25e1f5535a4", + "sha256:a4c555f658a7c135b8a22bd26c2e55cfaf5876e4d5962d8c25652f2addd556bc" ], "index": "pypi", - "version": "==0.38" + "version": "==0.39" }, "click": { "hashes": [ @@ -1390,10 +1395,10 @@ }, "importlib-metadata": { "hashes": [ - "sha256:027cfc6524613de726789072f95d2e4cc64dd1dee8096d42d13f2ead5bd302f5", - "sha256:0d05199e1f0b1a8707a1b9c46476d4a49807fb56cb1b0737db1d37feb42fe31d" + "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", + "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db" ], - "version": "==0.15" + "version": "==0.18" }, "isort": { "hashes": [ @@ -1452,10 +1457,10 @@ }, "marshmallow": { "hashes": [ - "sha256:0e497a6447ffaad55578138ca512752de7a48d12f444996ededc3d6bf8a09ca2", - "sha256:e21a4dea20deb167c723e0ffb13f4cf33bcbbeb8a334e92406a3308cedea2826" + "sha256:9cedfc5b6f568d57e8a2cf3d293fbd81b05e5ef557854008d03e25660a39ccfd", + "sha256:a4d99922116a76e5abd8f997ec0519086e24814b7e1e1344bebe2a312ba50235" ], - "version": "==2.19.2" + "version": "==2.19.5" }, "mock": { "hashes": [ @@ -1526,11 +1531,11 @@ }, "pytest": { "hashes": [ - "sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24", - "sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6" + "sha256:4a784f1d4f2ef198fe9b7aef793e9fa1a3b2f84e822d9b3a64a181293a572d45", + "sha256:926855726d8ae8371803f7b2e6ec0a69953d9c6311fa7c3b6c1b929ff92d27da" ], "index": "pypi", - "version": "==4.5.0" + "version": "==4.6.3" }, "pytest-cache": { "hashes": [ @@ -1629,11 +1634,11 @@ }, "sphinx": { "hashes": [ - "sha256:423280646fb37944dd3c85c58fb92a20d745793a9f6c511f59da82fa97cd404b", - "sha256:de930f42600a4fef993587633984cc5027dedba2464bcf00ddace26b40f8d9ce" + "sha256:22538e1bbe62b407cf5a8aabe1bb15848aa66bb79559f42f5202bbce6b757a69", + "sha256:f9a79e746b87921cabc3baa375199c6076d1270cee53915dbd24fdbeaaacc427" ], "index": "pypi", - "version": "==2.0.1" + "version": "==2.1.2" }, "sphinxcontrib-applehelp": { "hashes": [ diff --git a/pytest.ini b/pytest.ini index 229d56d95..e65fcf811 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,6 +7,8 @@ # more details. [pytest] -pep8ignore = docs/conf.py ALL +pep8ignore = + *.py W503 + docs/conf.py ALL addopts = --pep8 --doctest-glob="*.rst" --doctest-modules --cov=sonar --cov-report=term-missing --ignore=setup.py testpaths = docs tests sonar diff --git a/run-tests.sh b/run-tests.sh index 9e0002382..8056ad736 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -12,7 +12,7 @@ # released. pipenv check --ignore 36759 --ignore 36810 && \ pipenv run pydocstyle sonar tests docs && \ -pipenv run isort -rc -c -df && \ +pipenv run isort -rc -c -df --multi-line=3 --trailing-comma --force-grid-wrap=0 --use-parentheses --line-width=79 && \ pipenv run check-manifest --ignore ".travis-*,docs/_build*" && \ pipenv run sphinx-build -qnNW docs docs/_build/html && \ pipenv run test diff --git a/sonar/config.py b/sonar/config.py index e1ad7952e..6eae9fe92 100644 --- a/sonar/config.py +++ b/sonar/config.py @@ -269,15 +269,15 @@ def _(x): 'documents': dict( aggs=dict( institution=dict(terms=dict(field='institution.pid')), - language=dict(terms=dict(field='languages.language')), + language=dict(terms=dict(field='languages')), author=dict(terms=dict(field='facet_authors')), subject=dict(terms=dict(field='facet_subjects')) ), filters={ - _('institution'): terms_filter('institution.pid'), - _('language'): terms_filter('languages.language'), - _('author'): terms_filter('facet_authors'), - _('subject'): terms_filter('facet_subjects'), + 'institution': terms_filter('institution.pid'), + 'language': terms_filter('languages'), + 'author': terms_filter('facet_authors'), + 'subject': terms_filter('facet_subjects'), } ) } diff --git a/sonar/modules/config.py b/sonar/modules/config.py index 057d7ad67..8c776ed08 100644 --- a/sonar/modules/config.py +++ b/sonar/modules/config.py @@ -8,4 +8,4 @@ """SONAR configuration.""" -SONAR_APP_API_URL = 'https://localhost:5000/api/' +SONAR_APP_API_URL = "https://localhost:5000/api/" diff --git a/sonar/modules/documents/cli.py b/sonar/modules/documents/cli.py index a013d0e1d..979dd5ad4 100644 --- a/sonar/modules/documents/cli.py +++ b/sonar/modules/documents/cli.py @@ -8,9 +8,6 @@ """Documents CLI commands.""" -import json -from functools import partial - import click import requests from click.exceptions import ClickException @@ -19,7 +16,6 @@ from flask.cli import with_appcontext from invenio_db import db from invenio_indexer.api import RecordIndexer -from invenio_records import Record from sonar.modules.documents.dojson.contrib.marc21tojson import marc21tojson from sonar.modules.institutions.api import InstitutionRecord @@ -32,9 +28,9 @@ def documents(): """Documents CLI commands.""" -@documents.command('import') -@click.argument('institution') -@click.option('--pages', '-p', required=True, type=int, default=10) +@documents.command("import") +@click.argument("institution") +@click.option("--pages", "-p", required=True, type=int, default=10) @with_appcontext def import_documents(institution, pages): """Import documents from RERO doc. @@ -42,53 +38,64 @@ def import_documents(institution, pages): institution: String institution filter for retreiving documents pages: Number of pages to import """ - url = current_app.config.get('SONAR_DOCUMENTS_RERO_DOC_URL') + url = current_app.config.get("SONAR_DOCUMENTS_RERO_DOC_URL") click.secho( 'Importing {pages} pages of records for "{institution}" ' - 'from {url}'.format(pages=pages, institution=institution, url=url)) + "from {url}".format(pages=pages, institution=institution, url=url) + ) # Get institution record from database institution_record = InstitutionRecord.get_record_by_pid(institution) if not institution_record: - raise ClickException('Institution record not found in database') + raise ClickException("Institution record not found in database") institution_ref_link = InstitutionRecord.get_ref_link( - 'institutions', institution_record['pid']) + "institutions", institution_record["pid"] + ) # mapping between institution key and RERO doc filter institution_map = current_app.config.get( - 'SONAR_DOCUMENTS_INSTITUTIONS_MAP') + "SONAR_DOCUMENTS_INSTITUTIONS_MAP" + ) if not institution_map: - raise ClickException('Institution map not found in configuration') + raise ClickException("Institution map not found in configuration") if institution not in institution_map: raise ClickException( 'Institution map for "{institution}" not found in configuration, ' - 'keys available {keys}'.format( - institution=institution, - keys=institution_map.keys())) + "keys available {keys}".format( + institution=institution, keys=institution_map.keys() + ) + ) key = institution_map[institution] current_page = 1 indexer = RecordIndexer() - while(current_page <= pages): - click.echo('Importing records {start} to {end}... '.format( - start=(current_page*10-9), end=(current_page*10)), nl=False) + while current_page <= pages: + click.echo( + "Importing records {start} to {end}... ".format( + start=(current_page * 10 - 9), end=(current_page * 10) + ), + nl=False, + ) # Read Marc21 data for current page response = requests.get( - '{url}?of=xm&jrec={first_record}&c=NAVSITE.{institution}' - .format(url=url, - first_record=(current_page*10-9), - institution=key.upper()), stream=True) + "{url}?of=xm&jrec={first_record}&c=NAVSITE.{institution}".format( + url=url, + first_record=(current_page * 10 - 9), + institution=key.upper(), + ), + stream=True, + ) response.raw.decode_content = True - if(response.status_code != 200): + if response.status_code != 200: raise ClickException('Request to "{url}" failed'.format(url=url)) ids = [] @@ -101,7 +108,7 @@ def import_documents(institution, pages): record = marc21tojson.do(record) # Add institution - record['institution'] = {'$ref': institution_ref_link} + record["institution"] = {"$ref": institution_ref_link} # Register record to DB db_record = DocumentRecord.create(record) @@ -116,6 +123,6 @@ def import_documents(institution, pages): current_page += 1 - click.secho('Done', fg='green', nl=True) + click.secho("Done", fg="green", nl=True) - click.secho('Finished', fg='green') + click.secho("Finished", fg="green") diff --git a/sonar/modules/documents/dojson/contrib/marc21tojson/__init__.py b/sonar/modules/documents/dojson/contrib/marc21tojson/__init__.py index 6cc8ff2ad..afe3320c1 100644 --- a/sonar/modules/documents/dojson/contrib/marc21tojson/__init__.py +++ b/sonar/modules/documents/dojson/contrib/marc21tojson/__init__.py @@ -10,4 +10,4 @@ from .model import marc21tojson -__all__ = ('marc21tojson') +__all__ = "marc21tojson" diff --git a/sonar/modules/documents/dojson/contrib/marc21tojson/model.py b/sonar/modules/documents/dojson/contrib/marc21tojson/model.py index 19ee78c19..3cfa7b3da 100644 --- a/sonar/modules/documents/dojson/contrib/marc21tojson/model.py +++ b/sonar/modules/documents/dojson/contrib/marc21tojson/model.py @@ -8,6 +8,7 @@ """MARC21 model definition.""" +# pylint: disable=unused-argument import re import sys @@ -19,61 +20,59 @@ def remove_punctuation(data): """Remove punctuation from data.""" - try: - if data[-1:] == ',': - data = data[:-1] - if data[-2:] == ' :': - data = data[:-2] - if data[-2:] == ' ;': - data = data[:-2] - if data[-2:] == ' /': - data = data[:-2] - if data[-2:] == ' -': - data = data[:-2] - except Exception: - pass + if not isinstance(data, str): + return data + + if data[-1:] == ",": + data = data[:-1] + if data[-2:] == " :": + data = data[:-2] + if data[-2:] == " ;": + data = data[:-2] + if data[-2:] == " /": + data = data[:-2] + if data[-2:] == " -": + data = data[:-2] + return data -def get_mef_person_link(id, key, value): +def get_mef_person_link(person_id, key, value): """Get mef person link.""" # https://mef.test.rero.ch/api/mef/?q=rero.rero_pid:A012327677 - PROD_HOST = 'mef.rero.ch' - DEV_HOST = 'mef.test.rero.ch' + prod_host = "mef.rero.ch" + dev_host = "mef.test.rero.ch" mef_url = None - if id: - identifier = id[1:].split(')') + if person_id: + identifier = person_id[1:].split(")") url = "{mef}/?q={org}.{org}_pid:{pid}".format( - mef="https://{host}/api/mef".format(host=DEV_HOST), + mef="https://{host}/api/mef".format(host=dev_host), org=identifier[0].lower(), - pid=identifier[1] + pid=identifier[1], ) request = requests.get(url=url) if request.status_code == requests.codes.ok: data = request.json() - hits = data.get('hits', {}).get('hits') + hits = data.get("hits", {}).get("hits") if hits: - mef_url = hits[0].get('links').get('self') - mef_url = mef_url.replace(DEV_HOST, PROD_HOST) + mef_url = hits[0].get("links").get("self") + mef_url = mef_url.replace(dev_host, prod_host) else: print( - 'ERROR: MEF person not found', + "ERROR: MEF person not found", url, key, value, - file=sys.stderr + file=sys.stderr, ) else: print( - 'ERROR: MEF request', - url, - request.status_code, - file=sys.stderr + "ERROR: MEF request", url, request.status_code, file=sys.stderr ) return mef_url -@marc21tojson.over('type', 'leader') +@marc21tojson.over("type", "leader") def marc21_to_type(self, key, value): """ Get document type. @@ -86,34 +85,33 @@ def marc21_to_type(self, key, value): Sounds: LDR/6: i|j E-books (imported from Cantook) """ - type = None + document_type = None type_of_record = value[6] bibliographic_level = value[7] - if type_of_record == 'a': - if bibliographic_level == 'm': - type = 'book' - elif bibliographic_level == 's': - type = 'journal' - elif bibliographic_level == 'a': - type = 'article' - elif type_of_record in ['c', 'd']: - type = 'score' - elif type_of_record in ['i', 'j']: - type = 'sound' - elif type_of_record == 'g': - type = 'video' - # Todo 007 - return type - - -@marc21tojson.over('pid', '^001') + if type_of_record == "a": + if bibliographic_level == "m": + document_type = "book" + elif bibliographic_level == "s": + document_type = "journal" + elif bibliographic_level == "a": + document_type = "article" + elif type_of_record in ["c", "d"]: + document_type = "score" + elif type_of_record in ["i", "j"]: + document_type = "sound" + elif type_of_record == "g": + document_type = "video" + return document_type + + +@marc21tojson.over("pid", "^001") @utils.ignore_value def marc21_to_pid(self, key, value): """Get ID.""" return value -@marc21tojson.over('title', '^245..') +@marc21tojson.over("title", "^245..") @utils.ignore_value def marc21_to_title(self, key, value): """Get title. @@ -121,28 +119,28 @@ def marc21_to_title(self, key, value): title: 245$a without the punctuaction. If there's a $b, then 245$a : $b without the " /" """ - main_title = remove_punctuation(value.get('a')) - sub_title = value.get('b') + main_title = remove_punctuation(value.get("a")) + sub_title = value.get("b") # responsability = value.get('c') if sub_title: - main_title += ' : ' + ' : '.join( + main_title += " : " + " : ".join( utils.force_list(remove_punctuation(sub_title)) ) return main_title -@marc21tojson.over('titlesProper', '^730..') +@marc21tojson.over("titlesProper", "^730..") @utils.for_each_value @utils.ignore_value -def marc21_to_titlesProper(self, key, value): +def marc21_to_titles_proper(self, key, value): """Test dojson marc21titlesProper. titleProper: 730$a """ - return value.get('a') + return value.get("a") -@marc21tojson.over('languages', '^008') +@marc21tojson.over("languages", "^008") @utils.ignore_value def marc21_to_languages(self, key, value): """Get languages. @@ -150,24 +148,24 @@ def marc21_to_languages(self, key, value): languages: 008 and 041 [$a, repetitive] """ language = value.strip()[35:38] - to_return = [{'language': language}] + to_return = [language] return to_return -@marc21tojson.over('translatedFrom', '^041..') +@marc21tojson.over("translatedFrom", "^041..") @utils.ignore_value -def marc21_to_translatedFrom(self, key, value): +def marc21_to_translated_from(self, key, value): """Get translatedFrom. translatedFrom: 041 [$h repetitive] languages: 008 and 041 [$a, repetitive] """ - languages = self.get('languages', []) + languages = self.get("languages", []) unique_lang = [] if languages != []: - unique_lang.append(languages[0]['language']) + unique_lang.append(languages[0]) - language = value.get('a') + language = value.get("a") if language: for lang in utils.force_list(language): if lang not in unique_lang: @@ -175,17 +173,17 @@ def marc21_to_translatedFrom(self, key, value): languages = [] for lang in unique_lang: - languages.append({'language': lang}) + languages.append(lang) - self['languages'] = languages - translated = value.get('h') + self["languages"] = languages + translated = value.get("h") if translated: return list(utils.force_list(translated)) else: return None -@marc21tojson.over('authors', '[17][01]0..') +@marc21tojson.over("authors", "[17][01]0..") @utils.for_each_value @utils.ignore_value def marc21_to_author(self, key, value): @@ -199,79 +197,79 @@ def marc21_to_author(self, key, value): authors.qualifier: 100 $c or 700 $c (facultatif) authors.type: if 100 or 700 then person, if 710 then organisation """ - if not (key[4] == '2'): + if not key[4] == "2": author = {} - author['type'] = 'person' - if value.get('0'): - ref = get_mef_person_link(value.get('0'), key, value) + author["type"] = "person" + if value.get("0"): + ref = get_mef_person_link(value.get("0"), key, value) if ref: - author['$ref'] = ref + author["$ref"] = ref # we do not have a $ref - if not author.get('$ref'): - author['name'] = remove_punctuation(value.get('a')) - author_subs = utils.force_list(value.get('b')) + if not author.get("$ref"): + author["name"] = remove_punctuation(value.get("a")) + author_subs = utils.force_list(value.get("b")) if author_subs: for author_sub in author_subs: - author['name'] += ' ' + remove_punctuation(author_sub) - if key[:3] == '710': - author['type'] = 'organisation' + author["name"] += " " + remove_punctuation(author_sub) + if key[:3] == "710": + author["type"] = "organisation" else: - if value.get('c'): - author['qualifier'] = remove_punctuation(value.get('c')) - if value.get('d'): - author['date'] = remove_punctuation(value.get('d')) + if value.get("c"): + author["qualifier"] = remove_punctuation(value.get("c")) + if value.get("d"): + author["date"] = remove_punctuation(value.get("d")) return author else: return None -@marc21tojson.over('publishers', '^260..') +@marc21tojson.over("publishers", "^260..") @utils.ignore_value -def marc21_to_publishers_publicationDate(self, key, value): +def marc21_to_publishers_publication_date(self, key, value): """Get publisher. publisher.name: 260 [$b repetitive] (without the , but keep the ;) publisher.place: 260 [$a repetitive] (without the : but keep the ;) publicationDate: 260 [$c repetitive] (but take only the first one) """ - lasttag = '?' - publishers = self.get('publishers', []) + lasttag = "?" + publishers = self.get("publishers", []) publisher = {} indexes = {} - lasttag = '?' - for tag in value['__order__']: + lasttag = "?" + for tag in value["__order__"]: index = indexes.get(tag, 0) data = value[tag] - if type(data) == tuple: + if isinstance(data, tuple): data = data[index] - if tag == 'a' and index > 0 and lasttag != 'a': + if tag == "a" and index > 0 and lasttag != "a": publishers.append(remove_punctuation(publisher)) publisher = {} - if tag == 'a': - place = publisher.get('place', []) + if tag == "a": + place = publisher.get("place", []) place.append(remove_punctuation(data)) - publisher['place'] = place - elif tag == 'b': - name = publisher.get('name', []) + publisher["place"] = place + elif tag == "b": + name = publisher.get("name", []) name.append(remove_punctuation(data)) - publisher['name'] = name - elif tag == 'c' and index == 0: + publisher["name"] = name + elif tag == "c" and index == 0: # 4 digits - date = re.match(r'.*?(\d{4})', data).group(1) - self['publicationYear'] = int(date) + date = re.match(r".*?(\d{4})", data).group(1) + self["publicationYear"] = int(date) # create free form if different - if data != str(self['publicationYear']): - self['freeFormedPublicationDate'] = data + if data != str(self["publicationYear"]): + self["freeFormedPublicationDate"] = data indexes[tag] = index + 1 lasttag = tag publishers.append(publisher) return publishers -@marc21tojson.over('formats', '^300..') +@marc21tojson.over("formats", "^300..") @utils.ignore_value def marc21_to_description(self, key, value): """Get extent, otherMaterialCharacteristics, formats. @@ -280,27 +278,27 @@ def marc21_to_description(self, key, value): otherMaterialCharacteristics: 300$b (the first one if many) formats: 300 [$c repetitive] """ - if value.get('a'): - if not self.get('extent', None): - self['extent'] = remove_punctuation( - utils.force_list(value.get('a'))[0] + if value.get("a"): + if not self.get("extent", None): + self["extent"] = remove_punctuation( + utils.force_list(value.get("a"))[0] ) - if value.get('b'): - if self.get('otherMaterialCharacteristics', []) == []: - self['otherMaterialCharacteristics'] = remove_punctuation( - utils.force_list(value.get('b'))[0] + if value.get("b"): + if self.get("otherMaterialCharacteristics", []) == []: + self["otherMaterialCharacteristics"] = remove_punctuation( + utils.force_list(value.get("b"))[0] ) - if value.get('c'): - formats = self.get('formats', None) + if value.get("c"): + formats = self.get("formats", None) if not formats: - data = value.get('c') + data = value.get("c") formats = list(utils.force_list(data)) return formats else: return None -@marc21tojson.over('series', '^490..') +@marc21tojson.over("series", "^490..") @utils.for_each_value @utils.ignore_value def marc21_to_series(self, key, value): @@ -310,54 +308,57 @@ def marc21_to_series(self, key, value): series.number: [490$v repetitive] """ series = {} - name = value.get('a') + name = value.get("a") if name: - series['name'] = ', '.join(utils.force_list(name)) - number = value.get('v') + series["name"] = ", ".join(utils.force_list(name)) + number = value.get("v") if number: - series['number'] = ', '.join(utils.force_list(number)) + series["number"] = ", ".join(utils.force_list(number)) return series -@marc21tojson.over('abstracts', '^520..') -@utils.for_each_value +@marc21tojson.over("abstracts", "^520..") @utils.ignore_value def marc21_to_abstracts(self, key, value): """Get abstracts. abstract: [520$a repetitive] """ - return ', '.join(utils.force_list(value.get('a'))) + if not self.get("abstracts", None): + self["abstracts"] = {} + self["abstracts"][value.get("9")] = value.get("a") + return None -@marc21tojson.over('identifiers', '^020..') + +@marc21tojson.over("identifiers", "^020..") @utils.ignore_value def marc21_to_identifier_isbn(self, key, value): """Get identifier isbn. identifiers:isbn: 020$a """ - if value.get('a'): - identifiers = self.get('identifiers', {}) - identifiers['isbn'] = value.get('a') + if value.get("a"): + identifiers = self.get("identifiers", {}) + identifiers["isbn"] = value.get("a") return identifiers else: return None -@marc21tojson.over('identifiers', '^035..') +@marc21tojson.over("identifiers", "^035..") @utils.ignore_value -def marc21_to_identifier_reroID(self, key, value): +def marc21_to_identifier_rero_id(self, key, value): """Get identifier reroId. identifiers:reroID: 035$a """ - identifiers = self.get('identifiers', {}) - identifiers['reroID'] = value.get('a') + identifiers = self.get("identifiers", {}) + identifiers["reroID"] = value.get("a") return identifiers -@marc21tojson.over('notes', '^500..') +@marc21tojson.over("notes", "^500..") @utils.for_each_value @utils.ignore_value def marc21_to_notes(self, key, value): @@ -365,21 +366,21 @@ def marc21_to_notes(self, key, value): note: [500$a repetitive] """ - return value.get('a') + return value.get("a") -@marc21tojson.over('is_part_of', '^773..') +@marc21tojson.over("is_part_of", "^773..") @utils.ignore_value def marc21_to_is_part_of(self, key, value): """Get is_part_of. is_part_of: [773$t repetitive] """ - if not self.get('is_part_of', None): - return value.get('t') + if not self.get("is_part_of", None): + return value.get("t") -@marc21tojson.over('subjects', '^6....') +@marc21tojson.over("subjects", "^6....") @utils.ignore_value def marc21_to_subjects(self, key, value): """Get subjects. @@ -387,4 +388,8 @@ def marc21_to_subjects(self, key, value): subjects: 6xx [duplicates could exist between several vocabularies, if possible deduplicate] """ - return value.get('a').split(' ; ') + if not self.get("subjects", None): + self["subjects"] = {} + + self["subjects"][value.get("9")] = value.get("a").split(" ; ") + return None diff --git a/sonar/modules/documents/jsonschemas/documents/document-v1.0.0.json b/sonar/modules/documents/jsonschemas/documents/document-v1.0.0.json index 99589be0e..b167071f7 100644 --- a/sonar/modules/documents/jsonschemas/documents/document-v1.0.0.json +++ b/sonar/modules/documents/jsonschemas/documents/document-v1.0.0.json @@ -4,7 +4,10 @@ "additionalProperties": false, "title": "Schema for document", "type": "object", - "required": ["pid", "title"], + "required": [ + "pid", + "title" + ], "properties": { "$schema": { "type": "string", @@ -23,11 +26,14 @@ "abstracts": { "title": "Abstract", "description": "Abstract of the resource.", - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 3 + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^fre|eng|ger|ita|spa$": { + "title": "Abstract translation", + "description": "Translation for the abstract and language key.", + "type": "string" + } } }, "identifiers": { @@ -65,33 +71,14 @@ "uniqueItems": true, "minItems": 1, "items": { - "type": "object", - "required": [ - "language" - ], - "properties": { - "language": { - "title": "Language", - "description": "Required. Language of the resource, primary or not.", - "type": "string", - "default": "fre", - "validationMessage": "Required. Language of the resource, primary or not.", - "enum": [ - "fre", - "ger", - "eng", - "ita", - "spa", - "ara", - "chi", - "lat", - "heb", - "jpn", - "por", - "rus" - ] - } - } + "type": "string", + "enum": [ + "fre", + "ger", + "eng", + "ita", + "esp" + ] } }, "authors": { @@ -160,11 +147,18 @@ "subjects": { "title": "Subject", "description": "Subject of the resource.", - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^fre|eng|ger|ita|spa$": { + "title": "Subject translation", + "description": "Translation for the subject and language key.", + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } } }, "notes": { @@ -186,7 +180,9 @@ "type": "string" } }, - "required": ["$ref"] + "required": [ + "$ref" + ] } } } \ No newline at end of file diff --git a/sonar/modules/documents/loaders/__init__.py b/sonar/modules/documents/loaders/__init__.py index b686667f1..ccd75c5b7 100644 --- a/sonar/modules/documents/loaders/__init__.py +++ b/sonar/modules/documents/loaders/__init__.py @@ -10,13 +10,13 @@ from __future__ import absolute_import, print_function -from invenio_records_rest.loaders.marshmallow import json_patch_loader, \ - marshmallow_loader +from invenio_records_rest.loaders.marshmallow import ( + json_patch_loader, + marshmallow_loader, +) from ..marshmallow import DocumentMetadataSchemaV1 json_v1 = marshmallow_loader(DocumentMetadataSchemaV1) -__all__ = ( - 'json_v1', -) +__all__ = ("json_v1",) diff --git a/sonar/modules/documents/mappings/v6/documents/document-v1.0.0.json b/sonar/modules/documents/mappings/v6/documents/document-v1.0.0.json index 77ff05830..5c9916f88 100644 --- a/sonar/modules/documents/mappings/v6/documents/document-v1.0.0.json +++ b/sonar/modules/documents/mappings/v6/documents/document-v1.0.0.json @@ -15,11 +15,41 @@ "type": "text" }, "abstracts": { - "type": "text" + "type": "object", + "properties": { + "eng": { + "type": "text", + "analyzer": "english" + }, + "fre": { + "type": "text", + "analyzer": "french" + }, + "ger": { + "type": "text", + "analyzer": "german" + } + } }, "subjects": { - "type": "text", - "copy_to": "facet_subjects" + "type": "object", + "properties": { + "eng": { + "type": "text", + "analyzer": "english", + "copy_to": "facet_subjects" + }, + "fre": { + "type": "text", + "analyzer": "french", + "copy_to": "facet_subjects" + }, + "ger": { + "type": "text", + "analyzer": "german", + "copy_to": "facet_subjects" + } + } }, "facet_subjects": { "type": "keyword" @@ -39,12 +69,7 @@ } }, "languages": { - "type": "object", - "properties": { - "language": { - "type": "keyword" - } - } + "type": "keyword" }, "is_part_of": { "type": "text" @@ -87,4 +112,4 @@ } } } -} +} \ No newline at end of file diff --git a/sonar/modules/documents/marshmallow/json.py b/sonar/modules/documents/marshmallow/json.py index bcc61039c..703f629b3 100644 --- a/sonar/modules/documents/marshmallow/json.py +++ b/sonar/modules/documents/marshmallow/json.py @@ -10,10 +10,12 @@ from __future__ import absolute_import, print_function -from invenio_records_rest.schemas import Nested, StrictKeysMixin -from invenio_records_rest.schemas.fields import DateString, \ - PersistentIdentifier, SanitizedUnicode -from marshmallow import fields, missing, validate +from invenio_records_rest.schemas import StrictKeysMixin +from invenio_records_rest.schemas.fields import ( + PersistentIdentifier, + SanitizedUnicode, +) +from marshmallow import fields class DocumentMetadataSchemaV1(StrictKeysMixin): @@ -21,7 +23,7 @@ class DocumentMetadataSchemaV1(StrictKeysMixin): pid = PersistentIdentifier() title = SanitizedUnicode(required=True) - abstracts = fields.List(fields.Str()) + abstracts = fields.Dict() authors = fields.Dict(dump_only=True) institution = fields.Dict(dump_only=True) diff --git a/sonar/modules/documents/serializers/__init__.py b/sonar/modules/documents/serializers/__init__.py index 2b1bb451e..d59b5b4d8 100644 --- a/sonar/modules/documents/serializers/__init__.py +++ b/sonar/modules/documents/serializers/__init__.py @@ -11,8 +11,10 @@ from __future__ import absolute_import, print_function from invenio_records_rest.serializers.json import JSONSerializer -from invenio_records_rest.serializers.response import record_responsify, \ - search_responsify +from invenio_records_rest.serializers.response import ( + record_responsify, + search_responsify, +) from ..marshmallow import DocumentSchemaV1 @@ -24,12 +26,8 @@ # Records-REST serializers # ======================== #: JSON record serializer for individual records. -json_v1_response = record_responsify(json_v1, 'application/json') +json_v1_response = record_responsify(json_v1, "application/json") #: JSON record serializer for search results. -json_v1_search = search_responsify(json_v1, 'application/json') +json_v1_search = search_responsify(json_v1, "application/json") -__all__ = ( - 'json_v1', - 'json_v1_response', - 'json_v1_search', -) +__all__ = ("json_v1", "json_v1_response", "json_v1_search") diff --git a/sonar/modules/documents/static/templates/documents/search_ui/results.html b/sonar/modules/documents/static/templates/documents/search_ui/results.html index a696109ee..6a32e5cb8 100644 --- a/sonar/modules/documents/static/templates/documents/search_ui/results.html +++ b/sonar/modules/documents/static/templates/documents/search_ui/results.html @@ -9,10 +9,12 @@

{{record.metadata.institution.name}}

-

{{ record.metadata.title }}

-

{{author.name}}

-

- {{record.metadata.abstracts[0]}} +

{{ record.metadata.title }}

+

{{author.name}}

+

+ {{abstract}}


\ No newline at end of file diff --git a/sonar/modules/documents/templates/documents/record.html b/sonar/modules/documents/templates/documents/record.html index e90898633..9bf9d1c2e 100644 --- a/sonar/modules/documents/templates/documents/record.html +++ b/sonar/modules/documents/templates/documents/record.html @@ -9,38 +9,92 @@ {%- extends config.RECORDS_UI_BASE_TEMPLATE %} +{% from 'sonar/macros/macro.html' import dl %} + {%- macro record_content(data) %} - {% for key, value in data.items() recursive %} -
  • - {% if value is mapping %} - {{ key }}: -
      {{ loop(value.items()) }}
    - {% elif value is iterable and value is not string %} - {{ key }}: -
      - {% for item in value %} -
    1. - {% if item is mapping %} -
        - {{ record_content(item) }} -
      - {% else %} - {{ item }} - {% endif %} -
    2. - {% endfor %} -
    - {% else %} - {{ key }}: {{ value }} - {% endif %} +{% for key, value in data.items() recursive %} +
  • + {% if value is mapping %} + {{ key }}: +
      {{ loop(value.items()) }}
    + {% elif value is iterable and value is not string %} + {{ key }}: +
      + {% for item in value %} +
    1. + {% if item is mapping %} +
        + {{ record_content(item) }} +
      + {% else %} + {{ item }} + {% endif %}
    2. - {% endfor %} + {% endfor %} +
    + {% else %} + {{ key }}: {{ value }} + {% endif %} +
  • +{% endfor %} {%- endmacro %} {%- block page_body %} -

    {{record.title}}

    + +{% set record = record.replace_refs() %} +

    {{record.title}}

    +{% if record.institution %} +
    {{record.institution.name}}
    +{% endif %} + +
    +
    + + {% if record.authors|length > 0 %} + {{ dl(_('Authors'), record.authors | authors_format) }} + {% endif %} + + + {% if record.abstracts|length > 0 %} + {{ dl(_('Abstract'), record.abstracts | translate_content(current_i18n.language) | nl2br ) }} + {% endif %} + + + {% if record.extent or record.otherMaterialCharacteristics or record.formats %} + {% set formats = ', '.join(record.formats) %} + {% set description = ', '.join([record.extent, record.otherMaterialCharacteristics, formats]|select) %} + {{ dl(_('Physical description'), description) }} + {% endif %} + + + {% if record.subjects|length > 0 %} + {{ dl(_('Subject'), record.subjects | translate_content(current_i18n.language) | join(', ')) }} + {% endif %} + + + {% if record.languages|length > 0 %} + {{ dl(_('Language'), record.languages[0] | translate_language(current_i18n.language)) }} + {% endif %} + + + {% if record.notes|length > 0 %} + {{ dl(_('Notes'), record.notes | join("\n") | nl2br) }} + {% endif %} + + + {% if record.identifiers %} + {% if record.identifiers.isbn %} + {{ dl(_('ISBN'), record.identifiers.isbn) }} + {% endif %} + {% endif %} + + {% set link = url_for('invenio_records_ui.document', pid_value=record.pid, ir=ir, _external=True) %} + {{ dl(_('Permalink'), '' + link + '') }} +
    +
    +{%- endblock %} \ No newline at end of file diff --git a/sonar/modules/documents/views.py b/sonar/modules/documents/views.py index ded5fa413..938aef9c0 100644 --- a/sonar/modules/documents/views.py +++ b/sonar/modules/documents/views.py @@ -6,20 +6,22 @@ # and/or modify it under the terms of the MIT License; see LICENSE file for # more details. -"""Blueprint definitions.""" +"""blueprint definitions.""" from __future__ import absolute_import, print_function +from babel import Locale from flask import Blueprint, g, render_template +from pycountry import languages blueprint = Blueprint( - 'documents', + "documents", __name__, - template_folder='templates', - static_folder='static', - url_prefix='/organization/' + template_folder="templates", + static_folder="static", + url_prefix="/organization/", ) -"""Blueprint used for loading templates and static assets +"""blueprint used for loading templates and static assets The sole purpose of this blueprint is to ensure that Invenio can find the templates and static files located in the folders of the same names next to @@ -30,32 +32,121 @@ @blueprint.url_defaults def add_ir(endpoint, values): """Add default ir parameter.""" - values.setdefault('ir', 'sonar') + values.setdefault("ir", "sonar") @blueprint.url_value_preprocessor def pull_ir(endpoint, values): """Add ir parameter to global variables.""" - g.ir = values.pop('ir') + g.ir = values.pop("ir") -@blueprint.route('/') +@blueprint.route("/") def index(): """IR (and SONAR) home view.""" - return render_template('sonar/frontpage.html') + return render_template("sonar/frontpage.html") -@blueprint.route('/search') +@blueprint.route("/search") def search(): """IR search results.""" - search_hidden_params = {'institution': g.ir} \ - if 'ir' in g and g.ir != 'sonar' else None + search_hidden_params = ( + {"institution": g.ir} if "ir" in g and g.ir != "sonar" else None + ) - return render_template('sonar/search.html', - search_hidden_params=search_hidden_params) + return render_template( + "sonar/search.html", search_hidden_params=search_hidden_params + ) def detail(pid, record, template=None, **kwargs): """Search details.""" - g.ir = kwargs.get('ir') - return render_template('documents/record.html', pid=pid, record=record) + g.ir = kwargs.get("ir") + return render_template( + "documents/record.html", pid=pid, record=record, ir=g.ir + ) + + +@blueprint.app_template_filter() +def authors_format(authors): + """Format authors for template.""" + output = [] + for author in authors: + output.append(author["name"]) + + return " ; ".join(output) + + +@blueprint.app_template_filter() +def nl2br(string): + r"""Replace \n to
    .""" + return string.replace("\n", "
    ") + + +@blueprint.app_template_filter() +def translate_language(lang, in_lang): + """Return language full name for the current language in the in_lang value. + + For example, translate_language("fr", "de") will return "Französisch" + + :param lang: Bibliographic language to translate (english 3 positions) + :param in_lang: Target language for translation + :return str + """ + lang = get_code_from_bibliographic_language(lang) + + return Locale(lang).get_language_name(in_lang).capitalize() + + +@blueprint.app_template_filter() +def translate_content(records, locale): + """Translate record data for the given locale.""" + lang = get_bibliographic_code_from_language(locale) + + if lang not in records: + return next(iter(records.items()))[1] + + return records[lang] + + +def get_code_from_bibliographic_language(language_code): + """Return language code from bibliographic language. + + For example, get_language_code_from_bibliographic_language("ger") will + return "de" + + :param language_code: Bibliographic language + :return str + """ + language = languages.get(bibliographic=language_code) + + if not language: + return "en" + + return language.alpha_2 + + +def get_bibliographic_code_from_language(language_code): + """Return bibliographic language code from language. + + For example, get_bibliographic_code_from_language("de") will + return "ger" + + :param language_code: Bibliographic language + :return str + """ + language = languages.get(alpha_2=language_code) + + if not language: + raise Exception( + 'Language code not found for "{language_code}"'.format( + language_code=language_code + ) + ) + + try: + return language.bibliographic + except Exception: + return None + + return None diff --git a/sonar/modules/institutions/loaders/__init__.py b/sonar/modules/institutions/loaders/__init__.py index 95fa4830c..66ac97f3f 100644 --- a/sonar/modules/institutions/loaders/__init__.py +++ b/sonar/modules/institutions/loaders/__init__.py @@ -16,14 +16,14 @@ from __future__ import absolute_import, print_function -from invenio_records_rest.loaders.marshmallow import json_patch_loader, \ - marshmallow_loader +from invenio_records_rest.loaders.marshmallow import ( + json_patch_loader, + marshmallow_loader, +) from ..marshmallow import InstitutionMetadataSchemaV1 #: JSON loader using Marshmallow for data validation. json_v1 = marshmallow_loader(InstitutionMetadataSchemaV1) -__all__ = ( - 'json_v1', -) +__all__ = ("json_v1",) diff --git a/sonar/modules/institutions/marshmallow/json.py b/sonar/modules/institutions/marshmallow/json.py index 92ecef0a6..f50d2a703 100644 --- a/sonar/modules/institutions/marshmallow/json.py +++ b/sonar/modules/institutions/marshmallow/json.py @@ -11,8 +11,11 @@ from invenio_jsonschemas.proxies import current_jsonschemas from invenio_records_rest.schemas import Nested, StrictKeysMixin -from invenio_records_rest.schemas.fields import DateString, \ - PersistentIdentifier, SanitizedUnicode +from invenio_records_rest.schemas.fields import ( + DateString, + PersistentIdentifier, + SanitizedUnicode, +) from marshmallow import fields, missing, validate diff --git a/sonar/modules/institutions/serializers/__init__.py b/sonar/modules/institutions/serializers/__init__.py index 6f4bb27cf..8f7dc4772 100644 --- a/sonar/modules/institutions/serializers/__init__.py +++ b/sonar/modules/institutions/serializers/__init__.py @@ -10,8 +10,10 @@ from __future__ import absolute_import, print_function from invenio_records_rest.serializers.json import JSONSerializer -from invenio_records_rest.serializers.response import record_responsify, \ - search_responsify +from invenio_records_rest.serializers.response import ( + record_responsify, + search_responsify, +) from ..marshmallow import InstitutionSchemaV1 @@ -23,12 +25,8 @@ # Records-REST serializers # ======================== #: JSON record serializer for individual records. -json_v1_response = record_responsify(json_v1, 'application/json') +json_v1_response = record_responsify(json_v1, "application/json") #: JSON record serializer for search results. -json_v1_search = search_responsify(json_v1, 'application/json') +json_v1_search = search_responsify(json_v1, "application/json") -__all__ = ( - 'json_v1', - 'json_v1_response', - 'json_v1_search', -) +__all__ = ("json_v1", "json_v1_response", "json_v1_search") diff --git a/sonar/theme/templates/sonar/macros/macro.html b/sonar/theme/templates/sonar/macros/macro.html new file mode 100644 index 000000000..3600c675d --- /dev/null +++ b/sonar/theme/templates/sonar/macros/macro.html @@ -0,0 +1,8 @@ +{% macro dl(title, content) %} +
    + {{ title }}: +
    +
    + {{ content|safe }} +
    +{% endmacro %} \ No newline at end of file diff --git a/sonar/translations/de/LC_MESSAGES/messages.po b/sonar/translations/de/LC_MESSAGES/messages.po index 0739ac841..d502e2af2 100644 --- a/sonar/translations/de/LC_MESSAGES/messages.po +++ b/sonar/translations/de/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: sonar 0.0.1\n" "Report-Msgid-Bugs-To: software@rero.ch\n" -"POT-Creation-Date: 2019-06-19 12:13+0200\n" +"POT-Creation-Date: 2019-06-21 15:10+0200\n" "PO-Revision-Date: 2019-06-13 10:45+0200\n" "Last-Translator: FULL NAME \n" "Language: de\n" @@ -34,22 +34,6 @@ msgstr "Swiss Open Access Repository" msgid "Welcome to Swiss Open Access Repository!" msgstr "Willkommen im Swiss Open Access Repository!" -#: sonar/config.py:277 -msgid "institution" -msgstr "Institution" - -#: sonar/config.py:278 -msgid "language" -msgstr "Sprache" - -#: sonar/config.py:279 -msgid "author" -msgstr "Autor" - -#: sonar/config.py:280 -msgid "subject" -msgstr "Stichwort" - #: sonar/config.py:289 msgid "Best match" msgstr "Beste Übereinstimmung" @@ -58,6 +42,38 @@ msgstr "Beste Übereinstimmung" msgid "Most recent" msgstr "Neueste" +#: sonar/modules/documents/templates/documents/record.html:58 +msgid "Authors" +msgstr "Autoren" + +#: sonar/modules/documents/templates/documents/record.html:63 +msgid "Abstract" +msgstr "Zusammenfassung" + +#: sonar/modules/documents/templates/documents/record.html:70 +msgid "Physical description" +msgstr "Physische Beschreibung" + +#: sonar/modules/documents/templates/documents/record.html:75 +msgid "Subject" +msgstr "Subjekt" + +#: sonar/modules/documents/templates/documents/record.html:80 +msgid "Language" +msgstr "Sprache" + +#: sonar/modules/documents/templates/documents/record.html:85 +msgid "Notes" +msgstr "Notizen" + +#: sonar/modules/documents/templates/documents/record.html:91 +msgid "ISBN" +msgstr "ISBN" + +#: sonar/modules/documents/templates/documents/record.html:97 +msgid "Permalink" +msgstr "Permalink" + #: sonar/theme/templates/sonar/401.html:13 msgid "Unauthorized" msgstr "Nicht autorisiert" @@ -233,4 +249,3 @@ msgstr "Zurück zu SONAR" #: sonar/theme/templates/sonar/partial/navbar.html:44 msgid "Log in" msgstr "Einloggen" - diff --git a/sonar/translations/fr/LC_MESSAGES/messages.po b/sonar/translations/fr/LC_MESSAGES/messages.po index 3225924ec..b6dd0381c 100644 --- a/sonar/translations/fr/LC_MESSAGES/messages.po +++ b/sonar/translations/fr/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: sonar 0.0.1\n" "Report-Msgid-Bugs-To: software@rero.ch\n" -"POT-Creation-Date: 2019-06-19 12:13+0200\n" +"POT-Creation-Date: 2019-06-21 15:10+0200\n" "PO-Revision-Date: 2019-06-06 11:09+0200\n" "Last-Translator: FULL NAME \n" "Language: fr\n" @@ -34,22 +34,6 @@ msgstr "Swiss Open Access Repository" msgid "Welcome to Swiss Open Access Repository!" msgstr "Bienvenue sur Swiss Open Access Repository" -#: sonar/config.py:277 -msgid "institution" -msgstr "Institution" - -#: sonar/config.py:278 -msgid "language" -msgstr "Langue" - -#: sonar/config.py:279 -msgid "author" -msgstr "Author" - -#: sonar/config.py:280 -msgid "subject" -msgstr "Mot-clé" - #: sonar/config.py:289 msgid "Best match" msgstr "Meilleure correspondance" @@ -58,6 +42,38 @@ msgstr "Meilleure correspondance" msgid "Most recent" msgstr "Les plus récents" +#: sonar/modules/documents/templates/documents/record.html:58 +msgid "Authors" +msgstr "Auteurs" + +#: sonar/modules/documents/templates/documents/record.html:63 +msgid "Abstract" +msgstr "Résumé" + +#: sonar/modules/documents/templates/documents/record.html:70 +msgid "Physical description" +msgstr "Description" + +#: sonar/modules/documents/templates/documents/record.html:75 +msgid "Subject" +msgstr "Sujet" + +#: sonar/modules/documents/templates/documents/record.html:80 +msgid "Language" +msgstr "Langue" + +#: sonar/modules/documents/templates/documents/record.html:85 +msgid "Notes" +msgstr "Notes" + +#: sonar/modules/documents/templates/documents/record.html:91 +msgid "ISBN" +msgstr "ISBN" + +#: sonar/modules/documents/templates/documents/record.html:97 +msgid "Permalink" +msgstr "Lien permanent" + #: sonar/theme/templates/sonar/401.html:13 msgid "Unauthorized" msgstr "Non autorisé" @@ -232,4 +248,3 @@ msgstr "Retour à SONAR" #: sonar/theme/templates/sonar/partial/navbar.html:44 msgid "Log in" msgstr "Identification" - diff --git a/sonar/translations/messages.pot b/sonar/translations/messages.pot index ca02049bb..28438577b 100644 --- a/sonar/translations/messages.pot +++ b/sonar/translations/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: sonar 0.0.1\n" "Report-Msgid-Bugs-To: software@rero.ch\n" -"POT-Creation-Date: 2019-06-19 12:13+0200\n" +"POT-Creation-Date: 2019-06-21 15:10+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -33,28 +33,44 @@ msgstr "" msgid "Welcome to Swiss Open Access Repository!" msgstr "" -#: sonar/config.py:277 -msgid "institution" +#: sonar/config.py:289 +msgid "Best match" msgstr "" -#: sonar/config.py:278 -msgid "language" +#: sonar/config.py:295 +msgid "Most recent" msgstr "" -#: sonar/config.py:279 -msgid "author" +#: sonar/modules/documents/templates/documents/record.html:58 +msgid "Authors" msgstr "" -#: sonar/config.py:280 -msgid "subject" +#: sonar/modules/documents/templates/documents/record.html:63 +msgid "Abstract" msgstr "" -#: sonar/config.py:289 -msgid "Best match" +#: sonar/modules/documents/templates/documents/record.html:70 +msgid "Physical description" msgstr "" -#: sonar/config.py:295 -msgid "Most recent" +#: sonar/modules/documents/templates/documents/record.html:75 +msgid "Subject" +msgstr "" + +#: sonar/modules/documents/templates/documents/record.html:80 +msgid "Language" +msgstr "" + +#: sonar/modules/documents/templates/documents/record.html:85 +msgid "Notes" +msgstr "" + +#: sonar/modules/documents/templates/documents/record.html:91 +msgid "ISBN" +msgstr "" + +#: sonar/modules/documents/templates/documents/record.html:97 +msgid "Permalink" msgstr "" #: sonar/theme/templates/sonar/401.html:13 diff --git a/tests/api/test_api_simple_flow.py b/tests/api/test_api_simple_flow.py index 815a85900..664b5f146 100644 --- a/tests/api/test_api_simple_flow.py +++ b/tests/api/test_api_simple_flow.py @@ -15,18 +15,18 @@ def test_simple_flow(client): """Test simple flow using REST API.""" - headers = [('Content-Type', 'application/json')] + headers = [("Content-Type", "application/json")] data = { - 'title': 'The title of the record', - 'abstracts': ['Record summary'] - } - url = 'https://localhost:5000/documents/' + "title": "The title of the record", + "abstracts": {"fre": "Record summary"}, + } + url = "https://localhost:5000/documents/" # create a record response = client.post(url, data=json.dumps(data), headers=headers) assert response.status_code == 201 - current_search.flush_and_refresh('documents') + current_search.flush_and_refresh("documents") # retrieve record - res = client.get('https://localhost:5000/documents/1') + res = client.get("https://localhost:5000/documents/1") assert res.status_code == 200 diff --git a/tests/e2e/test_front_page.py b/tests/e2e/test_front_page.py index 49238a9a7..470c6003c 100644 --- a/tests/e2e/test_front_page.py +++ b/tests/e2e/test_front_page.py @@ -6,5 +6,5 @@ def test_frontpage(live_server, browser): """Test retrieval of front page.""" browser.get(url_for('invenio_theme_frontpage.index', _external=True)) - assert "Software under development !" == \ - browser.find_element_by_tag_name('h5').text + assert "Swiss Open Access Repository" == \ + browser.find_element_by_tag_name('img').get_attribute('alt') diff --git a/tests/ui/documents/test_documents_cli.py b/tests/ui/documents/test_documents_cli.py index 0d459e990..f37986199 100644 --- a/tests/ui/documents/test_documents_cli.py +++ b/tests/ui/documents/test_documents_cli.py @@ -8,41 +8,39 @@ """Test CLI for importing documents.""" -import click -import pytest -from click.exceptions import ClickException +import pytest # pylint: disable=unused-import from click.testing import CliRunner -from pytest_invenio.fixtures import script_info import sonar.modules.documents.cli as Cli from sonar.modules.institutions.api import InstitutionRecord -def test_import_documents(app, script_info): +def test_import_documents(app, script_info): # pylint: disable=unused-argument """Test import documents.""" runner = CliRunner() - result = runner.invoke(Cli.import_documents, ['test'], obj=script_info) - assert result.output.find( - 'Institution record not found in database') != -1 + result = runner.invoke(Cli.import_documents, ["test"], obj=script_info) + assert result.output.find("Institution record not found in database") != -1 - InstitutionRecord.create({ - "pid": "test", - "name": "Test" - }, dbcommit=True) + InstitutionRecord.create({"pid": "test", "name": "Test"}, dbcommit=True) - result = runner.invoke(Cli.import_documents, ['test'], obj=script_info) - assert result.output.find( - 'Institution map for "test" not found in configuration') != -1 + result = runner.invoke(Cli.import_documents, ["test"], obj=script_info) + assert ( + result.output.find( + 'Institution map for "test" not found in configuration' + ) + != -1 + ) - result = runner.invoke(Cli.import_documents, ['usi'], obj=script_info) + result = runner.invoke(Cli.import_documents, ["usi"], obj=script_info) assert result.exit_code == 1 - InstitutionRecord.create({ - "pid": "usi", - "name": "Università della Svizzera italiana" - }, dbcommit=True) + InstitutionRecord.create( + {"pid": "usi", "name": "Università della Svizzera italiana"}, + dbcommit=True, + ) result = runner.invoke( - Cli.import_documents, ['usi', '--pages=1'], obj=script_info) + Cli.import_documents, ["usi", "--pages=1"], obj=script_info + ) assert result.exit_code == 0 diff --git a/tests/ui/documents/test_documents_views.py b/tests/ui/documents/test_documents_views.py index 2e828b18a..8774ca66b 100644 --- a/tests/ui/documents/test_documents_views.py +++ b/tests/ui/documents/test_documents_views.py @@ -9,7 +9,7 @@ """Test documents views.""" import pytest -from flask import g, url_for +from flask import url_for import sonar.modules.documents.views as views from sonar.modules.documents.api import DocumentRecord @@ -23,21 +23,56 @@ def test_pull_ir(app): def test_index(client): """Test frontpage.""" assert isinstance(views.index(), str) - assert client.get('/').status_code == 200 + assert client.get("/").status_code == 200 def test_search(app, client): """Test search.""" assert isinstance(views.search(), str) - assert client.get( - url_for('invenio_search_ui.search')).status_code == 200 + assert client.get(url_for("invenio_search_ui.search")).status_code == 200 def test_detail(app, client): """Test document detail page.""" - record = DocumentRecord.create({ - "title": "The title of the record" - }, dbcommit=True) + DocumentRecord.create({"title": "The title of the record"}, dbcommit=True) # assert isinstance(views.detail('1', record, ir='sonar'), str) - assert client.get('/organization/sonar/documents/1').status_code == 200 + assert client.get("/organization/sonar/documents/1").status_code == 200 + + +def test_authors_format(): + """Test author format filter.""" + authors = [{"name": "John Newby"}, {"name": "Kevin Doner"}] + + assert views.authors_format(authors) == "John Newby ; Kevin Doner" + + +def test_nl2br(): + """Test nl2br conversion.""" + text = "Multiline text\nMultiline text" + assert views.nl2br(text) == "Multiline text
    Multiline text" + + +def test_translate_language(): + """Test language translation.""" + assert views.translate_language("fre", "en") == "French" + + +def test_translate_content(): + """Test content item translation.""" + records = {"eng": "Summary of content", "fre": "Résumé du contenu"} + assert views.translate_content(records, "fr") == "Résumé du contenu" + assert views.translate_content(records, "de") == "Summary of content" + assert views.translate_content(records, "pt") == "Summary of content" + + +def test_get_code_from_bibliographic_language(): + """Test bibliographic language code to alpha 2 code conversion.""" + assert views.get_code_from_bibliographic_language("ger") == "de" + assert views.get_code_from_bibliographic_language("por") == "en" + + +def test_get_bibliographic_code_from_language(): + """Test bibliographic language code to alpha 2 code conversion.""" + assert not views.get_bibliographic_code_from_language("aa") + assert views.get_bibliographic_code_from_language("de") == "ger" diff --git a/tests/ui/documents/test_marc21tojson.py b/tests/ui/documents/test_marc21tojson.py index b3be1adc6..edf6df3a1 100644 --- a/tests/ui/documents/test_marc21tojson.py +++ b/tests/ui/documents/test_marc21tojson.py @@ -8,7 +8,8 @@ """Test marc21 to json converter.""" -import pytest + +import pytest # pylint: disable=unused-import from dojson.contrib.marc21.utils import create_record import sonar.modules.documents.dojson.contrib.marc21tojson.model as model @@ -17,16 +18,17 @@ def test_remove_punctuation(): """Test remove punctuation from string.""" - assert model.remove_punctuation( - 'lorem ipsum - / ; :,') == 'lorem ipsum' + assert model.remove_punctuation("lorem ipsum - / ; :,") == "lorem ipsum" def test_get_mef_person_link(): """Test getting MEF link.""" - assert model.get_mef_person_link(None, '', '') is None + assert model.get_mef_person_link(None, "", "") is None - assert model.get_mef_person_link( - '(RERO)A012327677', '', '')[:28] == 'https://mef.rero.ch/api/mef/' + assert ( + model.get_mef_person_link("(RERO)A012327677", "", "")[:28] + == "https://mef.rero.ch/api/mef/" + ) def test_marc21_to_type(): @@ -48,7 +50,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'book' + assert data.get("type") == "book" marc21xml = """ @@ -57,7 +59,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'journal' + assert data.get("type") == "journal" marc21xml = """ @@ -66,7 +68,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'article' + assert data.get("type") == "article" marc21xml = """ @@ -75,7 +77,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'score' + assert data.get("type") == "score" marc21xml = """ 00501nda a2200133 a 4500 @@ -83,7 +85,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'score' + assert data.get("type") == "score" marc21xml = """ @@ -92,7 +94,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'sound' + assert data.get("type") == "sound" marc21xml = """ 00501nja a2200133 a 4500 @@ -100,7 +102,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'sound' + assert data.get("type") == "sound" marc21xml = """ @@ -109,7 +111,7 @@ def test_marc21_to_type(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('type') == 'video' + assert data.get("type") == "video" def test_marc21_to_title(): @@ -127,7 +129,7 @@ def test_marc21_to_title(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('title') == 'main title : subtitle' + assert data.get("title") == "main title : subtitle" # subfields $a $c marc21xml = """ @@ -139,7 +141,7 @@ def test_marc21_to_title(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('title') == 'main title' + assert data.get("title") == "main title" # subfield $a marc21xml = """ @@ -150,10 +152,10 @@ def test_marc21_to_title(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('title') == 'main title' + assert data.get("title") == "main title" -def test_marc21_to_titlesProper(): +def test_marc21_to_titles_proper(): """Test dojson marc21titlesProper.""" marc21xml = """ @@ -165,7 +167,7 @@ def test_marc21_to_titlesProper(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('titlesProper') == ['proper title'] + assert data.get("titlesProper") == ["proper title"] marc21xml = """ @@ -179,10 +181,11 @@ def test_marc21_to_titlesProper(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('titlesProper') == ['proper title', 'other proper title'] + assert data.get("titlesProper") == ["proper title", "other proper title"] -def test_marc21_to_languages(): +# pylint: disable=unused-argument +def test_marc21_to_languages(app): """Test dojson marc21languages.""" marc21xml = """ @@ -198,8 +201,8 @@ def test_marc21_to_languages(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('languages') == [{'language': 'ara'}, {'language': 'eng'}] - assert data.get('translatedFrom') == ['ita'] + assert data.get("languages") == ["ara", "eng"] + assert data.get("translatedFrom") == ["ita"] marc21xml = """ @@ -216,12 +219,8 @@ def test_marc21_to_languages(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('languages') == [ - {'language': 'ara'}, - {'language': 'eng'}, - {'language': 'fre'} - ] - assert data.get('translatedFrom') == ['ita', 'ger'] + assert data.get("languages") == ["ara", "eng", "fre"] + assert data.get("translatedFrom") == ["ita", "ger"] marc21xml = """ @@ -235,8 +234,8 @@ def test_marc21_to_languages(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('languages') == [{'language': 'ara'}, {'language': 'eng'}] - assert 'translatedFrom' not in data + assert data.get("languages") == ["ara", "eng"] + assert "translatedFrom" not in data def test_marc21_to_authors(): @@ -262,24 +261,21 @@ def test_marc21_to_authors(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - authors = data.get('authors') + authors = data.get("authors") assert authors == [ { - 'name': 'Jean-Paul II', - 'type': 'person', - 'date': '1954-', - 'qualifier': 'Pape' + "name": "Jean-Paul II", + "type": "person", + "date": "1954-", + "qualifier": "Pape", }, { - 'name': 'Dumont, Jean', - 'type': 'person', - 'date': '1921-2014', - 'qualifier': 'Historien' + "name": "Dumont, Jean", + "type": "person", + "date": "1921-2014", + "qualifier": "Historien", }, - { - 'name': 'RERO', - 'type': 'organisation' - } + {"name": "RERO", "type": "organisation"}, ] marc21xml = """ @@ -302,22 +298,19 @@ def test_marc21_to_authors(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - authors = data.get('authors') + authors = data.get("authors") assert authors == [ { - 'name': 'Jean-Paul II', - 'type': 'person', - 'date': '1954-', - 'qualifier': 'Pape' + "name": "Jean-Paul II", + "type": "person", + "date": "1954-", + "qualifier": "Pape", }, - { - 'name': 'RERO', - 'type': 'organisation' - } + {"name": "RERO", "type": "organisation"}, ] -def test_marc21_to_publishers_publicationDate(): +def test_marc21_to_publishers_publication_date(): """Test dojson publishers publicationDate.""" marc21xml = """ @@ -331,13 +324,10 @@ def test_marc21_to_publishers_publicationDate(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('publishers') == [ - { - 'place': ['Lausanne'], - 'name': ['Payot'], - } + assert data.get("publishers") == [ + {"place": ["Lausanne"], "name": ["Payot"]} ] - assert data.get('publicationYear') == 2015 + assert data.get("publicationYear") == 2015 marc21xml = """ @@ -351,13 +341,10 @@ def test_marc21_to_publishers_publicationDate(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('publishers') == [ - { - 'place': ['Paris', 'Lausanne'], - 'name': ['Payot'], - } + assert data.get("publishers") == [ + {"place": ["Paris", "Lausanne"], "name": ["Payot"]} ] - assert data.get('publicationYear') == 1920 + assert data.get("publicationYear") == 1920 marc21xml = """ @@ -372,18 +359,12 @@ def test_marc21_to_publishers_publicationDate(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('publishers') == [ - { - 'place': ['Paris'], - 'name': ['Champion'] - }, - { - 'place': ['Genève'], - 'name': ['Droz'] - } + assert data.get("publishers") == [ + {"place": ["Paris"], "name": ["Champion"]}, + {"place": ["Genève"], "name": ["Droz"]}, ] - assert data.get('freeFormedPublicationDate') == '1912-1955' - assert data.get('publicationYear') == 1912 + assert data.get("freeFormedPublicationDate") == "1912-1955" + assert data.get("publicationYear") == 1912 def test_marc21_to_description(): @@ -410,9 +391,9 @@ def test_marc21_to_description(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('extent') == '116 p.' - assert data.get('otherMaterialCharacteristics') == 'ill.' - assert data.get('formats') == ['22 cm'] + assert data.get("extent") == "116 p." + assert data.get("otherMaterialCharacteristics") == "ill." + assert data.get("formats") == ["22 cm"] marc21xml = """ @@ -431,9 +412,9 @@ def test_marc21_to_description(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('extent') == '116 p.' - assert data.get('otherMaterialCharacteristics') == 'ill.' - assert data.get('formats') == ['22 cm', '12 x 15'] + assert data.get("extent") == "116 p." + assert data.get("otherMaterialCharacteristics") == "ill." + assert data.get("formats") == ["22 cm", "12 x 15"] def test_marc21_to_series(): @@ -452,15 +433,9 @@ def test_marc21_to_series(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('series') == [ - { - 'name': 'Collection One', - 'number': '5' - }, - { - 'name': 'Collection Two', - 'number': '123' - } + assert data.get("series") == [ + {"name": "Collection One", "number": "5"}, + {"name": "Collection Two", "number": "123"}, ] @@ -470,13 +445,14 @@ def test_marc21_to_abstract(): marc21xml = """ + eng This book is about """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('abstracts') == ["This book is about"] + assert data.get("abstracts") == {"eng": "This book is about"} def test_marc21_to_identifiers(): @@ -490,7 +466,7 @@ def test_marc21_to_identifiers(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('identifiers') is None + assert data.get("identifiers") is None marc21xml = """ @@ -504,9 +480,9 @@ def test_marc21_to_identifiers(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('identifiers') == { - 'reroID': 'R123456789', - 'isbn': '9782370550163' + assert data.get("identifiers") == { + "reroID": "R123456789", + "isbn": "9782370550163", } @@ -525,7 +501,7 @@ def test_marc21_to_notes(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('notes') == ['note 1', 'note 2'] + assert data.get("notes") == ["note 1", "note 2"] def test_marc21_to_is_part_of(): @@ -541,22 +517,30 @@ def test_marc21_to_is_part_of(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('is_part_of') == 'Stuart Hall : critical dialogues' + assert data.get("is_part_of") == "Stuart Hall : critical dialogues" -def test_marc21_to_subjects(): +def test_marc21_to_subjects(app): """Test dojson subjects.""" marc21xml = """ - + + eng subject 1 ; subject 2 + + fre + sujet 1 ; sujet 2 + """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('subjects') == ['subject 1', 'subject 2'] + assert data.get("subjects") == { + "eng": ["subject 1", "subject 2"], + "fre": ["sujet 1", "sujet 2"], + } def test_marc21_to_pid(): @@ -568,4 +552,4 @@ def test_marc21_to_pid(): """ marc21json = create_record(marc21xml) data = marc21tojson.do(marc21json) - assert data.get('pid') == '4316' + assert data.get("pid") == "4316" diff --git a/tests/ui/institutions/test_institutions_cli.py b/tests/ui/institutions/test_institutions_cli.py index 01221103c..380351a6c 100644 --- a/tests/ui/institutions/test_institutions_cli.py +++ b/tests/ui/institutions/test_institutions_cli.py @@ -8,20 +8,19 @@ """Test CLI for importing documents.""" -import click from click.testing import CliRunner -from pytest_invenio.fixtures import script_info import sonar.modules.institutions.cli as Cli from sonar.modules.institutions.api import InstitutionRecord -def test_import_institutions(app, script_info): +def test_import_institutions( + app, script_info +): # pylint: disable=unused-argument """Test import institutions.""" - InstitutionRecord.create({ - "pid": "usi", - "name": "Università della Svizzera italiana" - }) + InstitutionRecord.create( + {"pid": "usi", "name": "Università della Svizzera italiana"} + ) runner = CliRunner() From 8fae377aa60ee7f26014b70129d23660baa9f7a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81bastien=20De=CC=81le=CC=80ze?= Date: Tue, 25 Jun 2019 09:53:29 +0200 Subject: [PATCH 3/4] authentication: ORCID OAuth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NEW OAuth authentication with ORCID api. Signed-off-by: Sébastien Délèze --- sonar/config.py | 233 ++++++++++-------- .../theme/templates/sonar/accounts/login.html | 38 +-- .../templates/sonar/accounts/signup.html | 52 ++-- sonar/theme/templates/sonar/oauth/signup.html | 44 ++++ sonar/theme/templates/sonar/page.html | 199 ++++++++------- .../sonar/partial/dropdown_user.html | 16 +- .../theme/templates/sonar/partial/navbar.html | 107 ++++---- sonar/translations/de/LC_MESSAGES/messages.po | 64 +++-- sonar/translations/fr/LC_MESSAGES/messages.po | 63 +++-- sonar/translations/messages.pot | 59 +++-- 10 files changed, 529 insertions(+), 346 deletions(-) create mode 100644 sonar/theme/templates/sonar/oauth/signup.html diff --git a/sonar/config.py b/sonar/config.py index 6eae9fe92..ce895a7b9 100644 --- a/sonar/config.py +++ b/sonar/config.py @@ -16,9 +16,11 @@ from __future__ import absolute_import, print_function +import os from datetime import timedelta from invenio_indexer.api import RecordIndexer +from invenio_oauthclient.contrib import orcid from invenio_records_rest.facets import terms_filter from invenio_records_rest.utils import allow_all, check_elasticsearch @@ -34,28 +36,25 @@ def _(x): # Rate limiting # ============= #: Storage for ratelimiter. -RATELIMIT_STORAGE_URL = 'redis://localhost:6379/3' +RATELIMIT_STORAGE_URL = "redis://localhost:6379/3" # I18N # ==== #: Default language -BABEL_DEFAULT_LANGUAGE = 'en' +BABEL_DEFAULT_LANGUAGE = "en" #: Default time zone -BABEL_DEFAULT_TIMEZONE = 'Europe/Zurich' +BABEL_DEFAULT_TIMEZONE = "Europe/Zurich" #: Other supported languages (do not include the default language in list). -I18N_LANGUAGES = [ - ('fr', _('French')), - ('de', _('German')) -] +I18N_LANGUAGES = [("fr", _("French")), ("de", _("German"))] # Base templates # ============== #: Global base template. -BASE_TEMPLATE = 'sonar/page.html' +BASE_TEMPLATE = "sonar/page.html" #: Cover page base template (used for e.g. login/sign-up). -COVER_TEMPLATE = 'sonar/page_cover.html' +COVER_TEMPLATE = "sonar/page_cover.html" #: Settings base template. -SETTINGS_TEMPLATE = 'sonar/page_settings.html' +SETTINGS_TEMPLATE = "sonar/page_settings.html" # Logging # ======= @@ -65,15 +64,15 @@ def _(x): # Theme configuration # =================== #: Site name -THEME_SITENAME = _('Swiss Open Access Repository') +THEME_SITENAME = _("Swiss Open Access Repository") #: Use default frontpage. THEME_FRONTPAGE = True #: Frontpage title. -THEME_FRONTPAGE_TITLE = _('Swiss Open Access Repository') +THEME_FRONTPAGE_TITLE = _("Swiss Open Access Repository") #: Frontpage template. -THEME_FRONTPAGE_TEMPLATE = 'sonar/frontpage.html' +THEME_FRONTPAGE_TEMPLATE = "sonar/frontpage.html" #: Theme logo -THEME_LOGO = 'images/sonar-logo.svg' +THEME_LOGO = "images/sonar-logo.svg" # Email configuration # =================== @@ -85,53 +84,54 @@ def _(x): # Assets # ====== #: Static files collection method (defaults to copying files). -COLLECT_STORAGE = 'flask_collect.storage.file' +COLLECT_STORAGE = "flask_collect.storage.file" # Accounts # ======== #: Email address used as sender of account registration emails. SECURITY_EMAIL_SENDER = SUPPORT_EMAIL #: Email subject for account registration emails. -SECURITY_EMAIL_SUBJECT_REGISTER = _( - "Welcome to Swiss Open Access Repository!") +SECURITY_EMAIL_SUBJECT_REGISTER = _("Welcome to Swiss Open Access Repository!") #: Redis session storage URL. -ACCOUNTS_SESSION_REDIS_URL = 'redis://localhost:6379/1' +ACCOUNTS_SESSION_REDIS_URL = "redis://localhost:6379/1" #: Enable session/user id request tracing. This feature will add X-Session-ID #: and X-User-ID headers to HTTP response. You MUST ensure that NGINX (or other #: proxies) removes these headers again before sending the response to the #: client. Set to False, in case of doubt. ACCOUNTS_USERINFO_HEADERS = True +# User profiles +USERPROFILES_EXTEND_SECURITY_FORMS = True + # Celery configuration # ==================== -BROKER_URL = 'amqp://guest:guest@localhost:5672/' +BROKER_URL = "amqp://guest:guest@localhost:5672/" #: URL of message broker for Celery (default is RabbitMQ). -CELERY_BROKER_URL = 'amqp://guest:guest@localhost:5672/' +CELERY_BROKER_URL = "amqp://guest:guest@localhost:5672/" #: URL of backend for result storage (default is Redis). -CELERY_RESULT_BACKEND = 'redis://localhost:6379/2' +CELERY_RESULT_BACKEND = "redis://localhost:6379/2" #: Scheduled tasks configuration (aka cronjobs). CELERY_BEAT_SCHEDULE = { - 'indexer': { - 'task': 'invenio_indexer.tasks.process_bulk_queue', - 'schedule': timedelta(minutes=5), + "indexer": { + "task": "invenio_indexer.tasks.process_bulk_queue", + "schedule": timedelta(minutes=5), }, - 'accounts': { - 'task': 'invenio_accounts.tasks.clean_session_table', - 'schedule': timedelta(minutes=60), + "accounts": { + "task": "invenio_accounts.tasks.clean_session_table", + "schedule": timedelta(minutes=60), }, } # Database # ======== #: Database URI including user and password -SQLALCHEMY_DATABASE_URI = \ - 'postgresql+psycopg2://sonar:sonar@localhost/sonar' +SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://sonar:sonar@localhost/sonar" # JSONSchemas # =========== #: Hostname used in URLs for local JSONSchemas. -JSONSCHEMAS_HOST = 'sonar.ch' +JSONSCHEMAS_HOST = "sonar.ch" # Flask configuration # =================== @@ -140,7 +140,7 @@ def _(x): #: Secret key - each installation (dev, production, ...) needs a separate key. #: It should be changed before deploying. -SECRET_KEY = 'CHANGE_ME' +SECRET_KEY = "CHANGE_ME" #: Max upload size for form data via application/mulitpart-formdata. MAX_CONTENT_LENGTH = 100 * 1024 * 1024 # 100 MiB #: Sets cookie with the secure flag by default @@ -149,12 +149,18 @@ def _(x): #: provided, the allowed hosts variable is set to localhost. In production it #: should be set to the correct host and it is strongly recommended to only #: route correct hosts to the application. -APP_ALLOWED_HOSTS = ['sonar.ch', 'localhost', '127.0.0.1'] +APP_ALLOWED_HOSTS = [ + "sonar.ch", + "localhost", + "127.0.0.1", + "sonardev.test.rero.ch", + "sonar.test.rero.ch", +] # OAI-PMH # ======= -OAISERVER_ID_PREFIX = 'oai:sonar.ch:' +OAISERVER_ID_PREFIX = "oai:sonar.ch:" # Debug # ===== @@ -165,120 +171,127 @@ def _(x): #: Switches off incept of redirects by Flask-DebugToolbar. DEBUG_TB_INTERCEPT_REDIRECTS = False -PIDSTORE_RECID_FIELD = 'pid' +PIDSTORE_RECID_FIELD = "pid" -SEARCH_UI_SEARCH_INDEX = 'documents' -SEARCH_UI_SEARCH_API = '/api/documents/' -SEARCH_UI_SEARCH_TEMPLATE = 'sonar/search.html' +SEARCH_UI_SEARCH_INDEX = "documents" +SEARCH_UI_SEARCH_API = "/api/documents/" +SEARCH_UI_SEARCH_TEMPLATE = "sonar/search.html" -SEARCH_UI_JSTEMPLATE_RESULTS = 'templates/documents/search_ui/results.html' -SEARCH_UI_JSTEMPLATE_COUNT = 'templates/documents/search_ui/count.html' -SEARCH_UI_JSTEMPLATE_PAGINATION = 'templates/documents/search_ui/'\ - 'pagination.html' -SEARCH_UI_JSTEMPLATE_SORT_ORDER = 'templates/documents/search_ui/'\ - 'sort_order.html' -SEARCH_UI_JSTEMPLATE_SELECT_BOX = 'templates/documents/search_ui/'\ - 'select_box.html' -SEARCH_UI_JSTEMPLATE_LOADING = 'templates/documents/search_ui/loading.html' -SEARCH_UI_JSTEMPLATE_FACETS = 'templates/documents/search_ui/facets.html' +SEARCH_UI_JSTEMPLATE_RESULTS = "templates/documents/search_ui/results.html" +SEARCH_UI_JSTEMPLATE_COUNT = "templates/documents/search_ui/count.html" +SEARCH_UI_JSTEMPLATE_PAGINATION = ( + "templates/documents/search_ui/" "pagination.html" +) +SEARCH_UI_JSTEMPLATE_SORT_ORDER = ( + "templates/documents/search_ui/" "sort_order.html" +) +SEARCH_UI_JSTEMPLATE_SELECT_BOX = ( + "templates/documents/search_ui/" "select_box.html" +) +SEARCH_UI_JSTEMPLATE_LOADING = "templates/documents/search_ui/loading.html" +SEARCH_UI_JSTEMPLATE_FACETS = "templates/documents/search_ui/facets.html" -SECURITY_LOGIN_USER_TEMPLATE = 'sonar/accounts/login.html' -SECURITY_FORGOT_PASSWORD_TEMPLATE = 'sonar/accounts/forgot_password.html' -SECURITY_REGISTER_USER_TEMPLATE = 'sonar/accounts/signup.html' +SECURITY_LOGIN_USER_TEMPLATE = "sonar/accounts/login.html" +SECURITY_FORGOT_PASSWORD_TEMPLATE = "sonar/accounts/forgot_password.html" +SECURITY_REGISTER_USER_TEMPLATE = "sonar/accounts/signup.html" RECORDS_UI_ENDPOINTS = { - 'document': { - 'pid_type': 'doc', - 'route': '/organization//documents/', - 'view_imp': 'sonar.modules.documents.views:detail' - }, + "document": { + "pid_type": "doc", + "route": "/organization//documents/", + "view_imp": "sonar.modules.documents.views:detail", + } } """Records UI for sonar.""" RECORDS_REST_ENDPOINTS = { - 'doc': dict( - pid_type='doc', - pid_minter='document_id', - pid_fetcher='document_id', + "doc": dict( + pid_type="doc", + pid_minter="document_id", + pid_fetcher="document_id", default_endpoint_prefix=True, record_class=DocumentRecord, search_class=DocumentSearch, indexer_class=RecordIndexer, - search_index='documents', + search_index="documents", search_type=None, record_serializers={ - 'application/json': ('sonar.modules.documents.serializers' - ':json_v1_response'), + "application/json": ( + "sonar.modules.documents.serializers" ":json_v1_response" + ) }, search_serializers={ - 'application/json': ('sonar.modules.documents.serializers' - ':json_v1_search'), + "application/json": ( + "sonar.modules.documents.serializers" ":json_v1_search" + ) }, record_loaders={ - 'application/json': ('sonar.modules.documents.loaders' - ':json_v1'), + "application/json": ("sonar.modules.documents.loaders" ":json_v1") }, - list_route='/documents/', - item_route='/documents/', - default_media_type='application/json', + list_route="/documents/", + item_route="/documents/", + default_media_type="application/json", max_result_window=10000, error_handlers=dict(), create_permission_factory_imp=allow_all, read_permission_factory_imp=check_elasticsearch, update_permission_factory_imp=allow_all, delete_permission_factory_imp=allow_all, - list_permission_factory_imp=allow_all + list_permission_factory_imp=allow_all, ), - 'inst': dict( - pid_type='inst', - pid_minter='institution_id', - pid_fetcher='institution_id', + "inst": dict( + pid_type="inst", + pid_minter="institution_id", + pid_fetcher="institution_id", default_endpoint_prefix=True, record_class=InstitutionRecord, search_class=InstitutionSearch, indexer_class=RecordIndexer, - search_index='institutions', + search_index="institutions", search_type=None, record_serializers={ - 'application/json': ('sonar.modules.institutions.serializers' - ':json_v1_response'), + "application/json": ( + "sonar.modules.institutions.serializers" ":json_v1_response" + ) }, search_serializers={ - 'application/json': ('sonar.modules.institutions.serializers' - ':json_v1_search'), + "application/json": ( + "sonar.modules.institutions.serializers" ":json_v1_search" + ) }, record_loaders={ - 'application/json': ('sonar.modules.institutions.loaders' - ':json_v1'), + "application/json": ( + "sonar.modules.institutions.loaders" ":json_v1" + ) }, - list_route='/institutions/', - item_route='/institutions/', - default_media_type='application/json', + list_route="/institutions/", + item_route="/institutions/", + default_media_type="application/json", max_result_window=10000, error_handlers=dict(), create_permission_factory_imp=allow_all, read_permission_factory_imp=check_elasticsearch, update_permission_factory_imp=allow_all, delete_permission_factory_imp=allow_all, - list_permission_factory_imp=allow_all - ) + list_permission_factory_imp=allow_all, + ), } """REST endpoints.""" RECORDS_REST_FACETS = { - 'documents': dict( + "documents": dict( aggs=dict( - institution=dict(terms=dict(field='institution.pid')), - language=dict(terms=dict(field='languages')), - author=dict(terms=dict(field='facet_authors')), - subject=dict(terms=dict(field='facet_subjects')) + institution=dict(terms=dict(field="institution.pid")), + language=dict(terms=dict(field="languages")), + author=dict(terms=dict(field="facet_authors")), + subject=dict(terms=dict(field="facet_subjects")), ), filters={ - 'institution': terms_filter('institution.pid'), - 'language': terms_filter('languages'), - 'author': terms_filter('facet_authors'), - 'subject': terms_filter('facet_subjects'), - } + "institution": terms_filter("institution.pid"), + "language": terms_filter("languages"), + "author": terms_filter("facet_authors"), + "subject": terms_filter("facet_subjects"), + }, ) } """REST search facets.""" @@ -286,15 +299,15 @@ def _(x): RECORDS_REST_SORT_OPTIONS = dict( documents=dict( bestmatch=dict( - title=_('Best match'), - fields=['_score'], - default_order='desc', + title=_("Best match"), + fields=["_score"], + default_order="desc", order=2, ), mostrecent=dict( - title=_('Most recent'), - fields=['-_created'], - default_order='asc', + title=_("Most recent"), + fields=["-_created"], + default_order="asc", order=1, ), ) @@ -302,10 +315,7 @@ def _(x): """Setup sorting options.""" RECORDS_REST_DEFAULT_SORT = dict( - documents=dict( - query='bestmatch', - noquery='mostrecent', - ), + documents=dict(query="bestmatch", noquery="mostrecent") ) """Set default sorting options.""" @@ -314,3 +324,14 @@ def _(x): JSONSCHEMAS_RESOLVE_SCHEMA = True JSONSCHEMAS_REPLACE_REFS = True + +OAUTHCLIENT_REMOTE_APPS = dict(orcid=orcid.REMOTE_SANDBOX_APP) +OAUTHCLIENT_SIGNUP_TEMPLATE = "sonar/oauth/signup.html" + +# Must be set as environment variable +ORCID_APP_CREDENTIALS = dict( + consumer_key=os.environ["INVENIO_ORCID_APP_CREDENTIALS_CONSUMER_KEY"], + consumer_secret=os.environ[ + "INVENIO_ORCID_APP_CREDENTIALS_CONSUMER_SECRET" + ], +) diff --git a/sonar/theme/templates/sonar/accounts/login.html b/sonar/theme/templates/sonar/accounts/login.html index 845f31f0a..b5c04c1f6 100644 --- a/sonar/theme/templates/sonar/accounts/login.html +++ b/sonar/theme/templates/sonar/accounts/login.html @@ -3,26 +3,36 @@ {% from "invenio_accounts/_macros.html" import render_field, form_errors %} {% block panel %} -
    -
    -
    -

    {{_('Log in to account') }}

    +
    +
    +
    +

    {{ _('Log in to account') }}

    {%- with form = login_user_form %} - {{form.hidden_tag()}} - {{form_errors(form)}} + {{ form.hidden_tag() }} + {{ form_errors(form) }} {{ render_field(form.email, autofocus=True, errormsg=False) }} {{ render_field(form.password, errormsg=False) }} - + {%- endwith %}
    -
    -
    -
    +
    + {{ _('Forgot password?') }} + {%- if security.registerable %} + {{ _('Sign Up') }} + {% endif %} +
    +
    {% endblock panel %} \ No newline at end of file diff --git a/sonar/theme/templates/sonar/accounts/signup.html b/sonar/theme/templates/sonar/accounts/signup.html index 07c046912..d11c49e6c 100644 --- a/sonar/theme/templates/sonar/accounts/signup.html +++ b/sonar/theme/templates/sonar/accounts/signup.html @@ -3,32 +3,44 @@ {% from "invenio_accounts/_macros.html" import render_field, form_errors %} {% block panel %} -
    -

    {% trans sitename=config.ACCOUNTS_SITENAME %}Sign up for a {{ sitename }} account!{% endtrans %}

    - {%- with form = register_user_form %} -
    - {{ form_errors(form) }} - {{ form.hidden_tag() }} - {%- block registration_form_fields scoped %} - {{ render_field(form.email, icon="glyphicon glyphicon-user", autofocus=True, errormsg=False) }} - {{ render_field(form.password, icon="glyphicon glyphicon-lock", errormsg=False) }} - {%- if form.password_confirm %} - {{ render_field(form.password_confirm, icon="glyphicon glyphicon-lock", errormsg=False) }} - {%- endif %} - {%- endblock registration_form_fields %} - {%- if form.recaptcha %} -
    {{ form.recaptcha() }}
    - {%- endif %} - -
    - {%- endwith %} +

    + {% trans sitename=config.ACCOUNTS_SITENAME %}Sign up for a {{ sitename }} account{% endtrans %} +

    + {%- with form = register_user_form %} +
    + {{ form_errors(form) }} + {{ form.hidden_tag() }} + {{ render_field(form.profile.username, errormsg=False) }} + {{ render_field(form.profile.full_name, errormsg=False) }} + {{ render_field(form.email, autofocus=True, errormsg=False) }} + {{ render_field(form.password, errormsg=False) }} + {%- if form.password_confirm %} + {{ render_field(form.password_confirm, errormsg=False) }} + {%- endif %} + + {%- if form.recaptcha %} +
    {{ form.recaptcha() }}
    + {%- endif %} + +
    + {%- endwith %} +
    +
    {% endblock panel %} \ No newline at end of file diff --git a/sonar/theme/templates/sonar/oauth/signup.html b/sonar/theme/templates/sonar/oauth/signup.html new file mode 100644 index 000000000..b3cff13b8 --- /dev/null +++ b/sonar/theme/templates/sonar/oauth/signup.html @@ -0,0 +1,44 @@ +{# -*- coding: utf-8 -*- + + This file is part of Invenio. + Copyright (C) 2015-2018 CERN. + + Invenio is free software; you can redistribute it and/or modify it + under the terms of the MIT License; see LICENSE file for more details. +#} + +{%- extends config.OAUTHCLIENT_SETTINGS_TEMPLATE %} + +{% from "invenio_oauthclient/_macros.html" import render_field, form_errors %} + +{% block page_body %} +
    + {%- block signup_panel %} +
    + {%- block signup_header %} + {%- block signup_app_icon %} + {% if app_icon -%} + + {%- endif %} + {%- endblock %} +

    + {% trans type=app_title %}Sign-up with {{ type }}{% endtrans %} +

    +

    {{ _('Fill in your details to complete your registration. You only have to do this once.') }}

    +
    + {% endblock %} + {%- block signup_form %} +
    + {{ form_errors(form) }} + {{ form.hidden_tag() }} + {{ render_field(form.profile.full_name) }} + {{ render_field(form.profile.username) }} + {{ render_field(form.email) }} + +
    + {%- endblock %} +
    + {%- endblock %} +
    +{% endblock %} \ No newline at end of file diff --git a/sonar/theme/templates/sonar/page.html b/sonar/theme/templates/sonar/page.html index d2bf017e3..6992f1ff2 100755 --- a/sonar/theme/templates/sonar/page.html +++ b/sonar/theme/templates/sonar/page.html @@ -10,103 +10,112 @@ {% set page = page|default('int') %} - - - {%- block head %} - {%- block head_meta %} - - - - {%- if description %} - - {% endif %} - {%- if keywords %} - - {% endif %} - {%- if config.get('THEME_GOOGLE_SITE_VERIFICATION', None) %} - {%- for google_id in config.THEME_GOOGLE_SITE_VERIFICATION %} - - {%- endfor %} - {%- endif %} - {%- endblock head_meta %} - - {%- block head_title %} - {%- set title = title or _(config.THEME_SITENAME) or _('Invenio') %} - {{title}} - {%- endblock head_title %} - - {%- block head_links %} - - {%- if keywords %} - - {% endif %} - - {%- block head_links_langs %} - {%- if alternate_urls %} - {%- for alt_ln, alternate_url in alternate_urls.items() %} - - {%- endfor %} - {%- endif %} - {%- endblock %} - - {%- block head_apple_icons %} - {%- for size in [144, 114, 72, 57] %} - {%- set icon_name = 'apple-touch-icon-%d-precomposed.png' | format(size) %} - - {%- endfor %} - {%- endblock head_apple_icons %} - - {%- endblock head_links %} - - {%- block css %} - {{ webpack[g.ir|default('sonar') ~ '-theme.css'] }} - {# assets "invenio_theme_css" %}{% endassets #} - - + - {%- endblock css %} - - {%- endblock head %} - - - {%- block body %} - {%- block browserupgrade %} - - {%- endblock browserupgrade %} - - {%- block body_inner %} -
    - {% include 'sonar/partial/navbar.html' %} - {%- block header %}{%- endblock header %} -
    - -
    - {%- block breadcrumbs %} - {%- include "sonar/breadcrumbs.html" %} - {%- endblock breadcrumbs %} - - {%- block flashmessages %} - {%- from "sonar/macros/messages.html" import flashed_messages with context -%} - {{ flashed_messages() }} - {%- endblock %} - - {%- block page_body %}{%- endblock page_body %} -
    - - {% include 'sonar/footer.html' %} - {%- endblock body_inner %} - - {%- block javascript %} - {% include 'sonar/javascript.html' %} - {%- endblock javascript %} - - {%- block trackingcode %} - {% include config.THEME_TRACKINGCODE_TEMPLATE %} - {%- endblock %} - {%- endblock body %} - - + {%- endblock browserupgrade %} + + {%- block body_inner %} +
    + {% include 'sonar/partial/navbar.html' %} + {%- block header %}{%- endblock header %} +
    + +
    + {%- block breadcrumbs %} + {%- include "sonar/breadcrumbs.html" %} + {%- endblock breadcrumbs %} + + {%- block flashmessages %} +
    + {%- from "sonar/macros/messages.html" import flashed_messages with context -%} + {{ flashed_messages() }} +
    + {%- endblock %} + + {%- block page_body %}{%- endblock page_body %} +
    + + {% include 'sonar/footer.html' %} + {%- endblock body_inner %} + + {%- block javascript %} + {% include 'sonar/javascript.html' %} + {%- endblock javascript %} + + {%- block trackingcode %} + {% include config.THEME_TRACKINGCODE_TEMPLATE %} + {%- endblock %} + {%- endblock body %} + + + \ No newline at end of file diff --git a/sonar/theme/templates/sonar/partial/dropdown_user.html b/sonar/theme/templates/sonar/partial/dropdown_user.html index e1ff8a098..2e24a831b 100644 --- a/sonar/theme/templates/sonar/partial/dropdown_user.html +++ b/sonar/theme/templates/sonar/partial/dropdown_user.html @@ -1,9 +1,11 @@ -