From 4b770461eb33494914b1ca830652d57a45ded216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Marie=CC=81thoz?= Date: Thu, 11 Feb 2021 16:17:38 +0100 Subject: [PATCH] user: add new fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replaces the phone field by 4 new home, buisness, mobile, other phone fields in the user data model. * Adds country, gender fields in the user data model. * Adds 3 invenio-userprofiles configuration to specify the list of countries, the default country and the read only fields. * Makes the barcode repetitive. * Removes all the user from the patron data model. * Adds the secound address, the local code, the source fields in the patron data model. Co-Authored-by: Johnny MarieĢthoz --- data/templates.json | 6 +- data/users.json | 214 +++++++++++------- pyproject.toml | 2 +- rero_ils/config.py | 7 +- rero_ils/dojson/utils.py | 2 +- rero_ils/modules/items/api/circulation.py | 2 +- rero_ils/modules/items/api/record.py | 4 +- rero_ils/modules/items/cli.py | 4 +- rero_ils/modules/loans/api.py | 2 +- rero_ils/modules/loans/cli.py | 4 +- rero_ils/modules/patrons/api.py | 117 +++------- rero_ils/modules/patrons/cli.py | 3 +- .../jsonschemas/patrons/patron-v0.0.1.json | 194 +++++++--------- rero_ils/modules/patrons/listener.py | 14 +- .../mappings/v7/patrons/patron-v0.0.1.json | 30 ++- rero_ils/modules/patrons/tasks.py | 2 +- .../rero_ils/_patron_profile_personal.html | 4 +- .../templates/rero_ils/patron_profile.html | 2 +- rero_ils/modules/users/api.py | 43 +++- .../users/jsonschemas/users/user-v0.0.1.json | 78 ++++++- rero_ils/modules/utils.py | 21 +- .../mappings/v7/vendors/vendor-v0.0.1.json | 4 +- rero_ils/utils.py | 19 +- tests/api/documents/test_marcxml_rest_api.py | 2 +- tests/api/items/test_items_rest.py | 4 +- tests/api/loans/test_loans_rest.py | 2 +- tests/api/patrons/test_patrons_blocked.py | 4 +- tests/api/patrons/test_patrons_rest.py | 39 ++-- tests/api/patrons/test_patrons_views.py | 2 +- tests/api/selfcheck/test_selfcheck.py | 16 +- tests/api/test_availability.py | 16 +- tests/api/test_permissions_librarian.py | 9 +- tests/api/test_permissions_patron.py | 4 +- tests/api/test_permissions_sys_librarian.py | 4 +- tests/api/test_search.py | 2 +- tests/api/test_user_authentication.py | 16 +- tests/api/users/test_users_api.py | 7 +- tests/data/data.json | 46 ++-- tests/e2e/cypress/cypress/fixtures/users.json | 6 +- tests/e2e/cypress/cypress/support/api.js | 6 +- tests/ui/circulation/test_actions_checkout.py | 4 +- tests/ui/circulation/test_inhouse_cipo.py | 2 +- tests/ui/patrons/test_patrons_api.py | 21 +- tests/unit/conftest.py | 4 +- tests/unit/test_cli.py | 2 +- tests/unit/test_patrons_jsonschema.py | 18 +- tests/unit/test_users_jsonschema.py | 7 +- tests/utils.py | 4 +- 48 files changed, 589 insertions(+), 436 deletions(-) diff --git a/data/templates.json b/data/templates.json index 242580d636..76296ae157 100644 --- a/data/templates.json +++ b/data/templates.json @@ -282,7 +282,7 @@ "patron_type": { "$ref": "https://ils.rero.ch/api/patron_types/1" }, - "phone": "", + "home_phone": "", "postal_code": "", "roles": [ "patron" @@ -861,7 +861,7 @@ "patron_type": { "$ref": "https://ils.rero.ch/api/patron_types/4" }, - "phone": "", + "home_phone": "", "postal_code": "", "roles": [ "patron" @@ -1440,7 +1440,7 @@ "patron_type": { "$ref": "https://ils.rero.ch/api/patron_types/5" }, - "phone": "", + "home_phone": "", "postal_code": "", "roles": [ "patron" diff --git a/data/users.json b/data/users.json index 523b97c49d..8367dd24a6 100644 --- a/data/users.json +++ b/data/users.json @@ -16,7 +16,7 @@ "$ref": "https://ils.rero.ch/api/libraries/4" } ], - "phone": "+39324993597", + "home_phone": "+39324993597", "postal_code": "11100", "street": "Viale Rue Gran Paradiso, 44", "notes": [ @@ -45,7 +45,7 @@ "$ref": "https://ils.rero.ch/api/libraries/4" } ], - "phone": "+41261234567", + "home_phone": "+41261234567", "postal_code": "1700", "street": "Avenue de la Gare, 80" }, @@ -65,7 +65,7 @@ "$ref": "https://ils.rero.ch/api/libraries/3" } ], - "phone": "+41911234567", + "home_phone": "+41911234567", "postal_code": "6900", "street": "Viale Carlo Cattaneo, 52" }, @@ -80,14 +80,16 @@ "patron" ], "last_name": "Casalini", - "phone": "+39324993585", + "home_phone": "+39324993585", "postal_code": "11100", "street": "Via Croix Noire 3", "patron": { "expiration_date": "2023-10-07", "blocked": false, "keep_history": false, - "barcode": "2050124311", + "barcode": [ + "2050124311" + ], "type": { "$ref": "https://ils.rero.ch/api/patron_types/1" }, @@ -120,7 +122,9 @@ "street": "Panoramica Collinare, 47", "patron": { "expiration_date": "2023-10-07", - "barcode": "2030124287", + "barcode": [ + "2030124287" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/2" @@ -145,7 +149,7 @@ "$ref": "https://ils.rero.ch/api/libraries/4" } ], - "phone": "+39244857275", + "home_phone": "+39244857275", "postal_code": "11010", "street": "Frazione Plan" }, @@ -164,7 +168,9 @@ "street": "Rue du Nord 7", "patron": { "expiration_date": "2023-10-07", - "barcode": "reroils1", + "barcode": [ + "reroils1" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/3" @@ -184,12 +190,14 @@ "patron" ], "last_name": "John", - "phone": "+1408492280015", + "home_phone": "+1408492280015", "postal_code": "95054", "street": "520 Scott Blvd", "patron": { "expiration_date": "2023-10-07", - "barcode": "2000000001", + "barcode": [ + "2000000001" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/3" @@ -209,12 +217,14 @@ "patron" ], "last_name": "Premand", - "phone": "+41795762233", + "home_phone": "+41795762233", "postal_code": "1892", "street": "Route du Village 6", "patron": { "expiration_date": "2023-10-07", - "barcode": "10000001", + "barcode": [ + "10000001" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/1" @@ -234,12 +244,14 @@ "patron" ], "last_name": "Broglio", - "phone": "+41918264239", + "home_phone": "+41918264239", "postal_code": "6500", "street": "Piazza Collegiata 12", "patron": { "expiration_date": "2023-10-07", - "barcode": "2050124312", + "barcode": [ + "2050124312" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/1" @@ -259,12 +271,14 @@ "patron" ], "last_name": "Carron", - "phone": "+41792001020", + "home_phone": "+41792001020", "postal_code": "1920", "street": "Gare 45", "patron": { "expiration_date": "2023-10-07", - "barcode": "kad001", + "barcode": [ + "kad001" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/1" @@ -284,12 +298,14 @@ "patron" ], "last_name": "Rard", - "phone": "+41792500512", + "home_phone": "+41792500512", "postal_code": "1926", "street": "Vignettes 25", "patron": { "expiration_date": "2023-10-07", - "barcode": "kad002", + "barcode": [ + "kad002" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/1" @@ -315,7 +331,7 @@ "$ref": "https://ils.rero.ch/api/libraries/5" } ], - "phone": "+01411234567", + "home_phone": "+01411234567", "postal_code": "0000", "street": "High Street" }, @@ -334,7 +350,9 @@ "street": "Magic Street 3", "patron": { "expiration_date": "2023-10-07", - "barcode": "3050124311", + "barcode": [ + "3050124311" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/4" @@ -358,7 +376,9 @@ "street": "Diagon Alley 72", "patron": { "expiration_date": "2023-10-07", - "barcode": "3050124312", + "barcode": [ + "3050124312" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/4" @@ -384,7 +404,7 @@ "$ref": "https://ils.rero.ch/api/libraries/6" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "7878", "street": "Imagination Street 48" }, @@ -404,7 +424,7 @@ "$ref": "https://ils.rero.ch/api/libraries/6" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -424,7 +444,7 @@ "$ref": "https://ils.rero.ch/api/libraries/7" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -444,7 +464,7 @@ "$ref": "https://ils.rero.ch/api/libraries/8" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -464,7 +484,7 @@ "$ref": "https://ils.rero.ch/api/libraries/9" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -484,7 +504,7 @@ "$ref": "https://ils.rero.ch/api/libraries/10" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -504,7 +524,7 @@ "$ref": "https://ils.rero.ch/api/libraries/11" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -524,7 +544,7 @@ "$ref": "https://ils.rero.ch/api/libraries/12" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -544,7 +564,7 @@ "$ref": "https://ils.rero.ch/api/libraries/13" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -564,7 +584,7 @@ "$ref": "https://ils.rero.ch/api/libraries/14" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -584,7 +604,7 @@ "$ref": "https://ils.rero.ch/api/libraries/15" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -604,7 +624,7 @@ "$ref": "https://ils.rero.ch/api/libraries/16" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -624,7 +644,7 @@ "$ref": "https://ils.rero.ch/api/libraries/17" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -644,7 +664,7 @@ "$ref": "https://ils.rero.ch/api/libraries/18" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -664,7 +684,7 @@ "$ref": "https://ils.rero.ch/api/libraries/19" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -684,7 +704,7 @@ "$ref": "https://ils.rero.ch/api/libraries/20" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -704,7 +724,7 @@ "$ref": "https://ils.rero.ch/api/libraries/21" } ], - "phone": "+765554433", + "home_phone": "+765554433", "postal_code": "no indication", "street": "no indication" }, @@ -719,12 +739,14 @@ "patron" ], "last_name": "Rh\u00e9aume", - "phone": "+41316126321", + "home_phone": "+41316126321", "postal_code": "1292", "street": "Faubourg du Lac 6", "patron": { "expiration_date": "2023-10-07", - "barcode": "920001", + "barcode": [ + "920001" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -744,12 +766,14 @@ "patron" ], "last_name": "Rocher", - "phone": "+41913843044", + "home_phone": "+41913843044", "postal_code": "1489", "street": "Rue du Tr\u00e9sor 9", "patron": { "expiration_date": "2023-10-07", - "barcode": "920002", + "barcode": [ + "920002" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -769,12 +793,14 @@ "patron" ], "last_name": "Chalifour", - "phone": "+41628609930", + "home_phone": "+41628609930", "postal_code": "2953", "street": "Rue de l'Industrie 46", "patron": { "expiration_date": "2023-10-07", - "barcode": "920003", + "barcode": [ + "920003" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -794,12 +820,14 @@ "patron" ], "last_name": "Lamarre", - "phone": "+41613956062", + "home_phone": "+41613956062", "postal_code": "2000", "street": "Avenue Max-Huber 2", "patron": { "expiration_date": "2023-10-07", - "barcode": "920004", + "barcode": [ + "920004" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -819,12 +847,14 @@ "patron" ], "last_name": "Boileau", - "phone": "+41815333591", + "home_phone": "+41815333591", "postal_code": "3960", "street": "Rue de Montsalvens 3", "patron": { "expiration_date": "2023-10-07", - "barcode": "920005", + "barcode": [ + "920005" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -844,12 +874,14 @@ "patron" ], "last_name": "Doiron", - "phone": "+41316126321", + "home_phone": "+41316126321", "postal_code": "1630", "street": "Rue du Vieux-Pont 3", "patron": { "expiration_date": "2023-10-07", - "barcode": "920006", + "barcode": [ + "920006" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -869,12 +901,14 @@ "patron" ], "last_name": "Beich", - "phone": "+41562747247", + "home_phone": "+41562747247", "postal_code": "9604", "street": "Seefeldstrasse 37", "patron": { "expiration_date": "2023-10-07", - "barcode": "920007", + "barcode": [ + "920007" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -894,12 +928,14 @@ "patron" ], "last_name": "Fischer", - "phone": "+41449166744", + "home_phone": "+41449166744", "postal_code": "8620", "street": "In Stierwisen 98", "patron": { "expiration_date": "2023-10-07", - "barcode": "920008", + "barcode": [ + "920008" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -919,12 +955,14 @@ "patron" ], "last_name": "Freeh", - "phone": "+41523734083", + "home_phone": "+41523734083", "postal_code": "8155", "street": "Im Wingert 117", "patron": { "expiration_date": "2023-10-07", - "barcode": "920009", + "barcode": [ + "920009" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -944,12 +982,14 @@ "patron" ], "last_name": "Schuhmacher", - "phone": "+41318033434", + "home_phone": "+41318033434", "postal_code": "9525", "street": "Werkstrasse 136", "patron": { "expiration_date": "2023-10-07", - "barcode": "920010", + "barcode": [ + "920010" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -969,12 +1009,14 @@ "patron" ], "last_name": "Trommler", - "phone": "+41278023434", + "home_phone": "+41278023434", "postal_code": "3605", "street": "L\u00fctzelfl\u00fchstrasse 120", "patron": { "expiration_date": "2023-10-07", - "barcode": "920011", + "barcode": [ + "920011" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -994,12 +1036,14 @@ "patron" ], "last_name": "Ziegler", - "phone": "+41222747247", + "home_phone": "+41222747247", "postal_code": "5637", "street": "\u00dcerklisweg 55", "patron": { "expiration_date": "2023-10-07", - "barcode": "920012", + "barcode": [ + "920012" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -1019,12 +1063,14 @@ "patron" ], "last_name": "Marcelo", - "phone": "+41322747447", + "home_phone": "+41322747447", "postal_code": "6675", "street": "Via Verbano 109", "patron": { "expiration_date": "2023-10-07", - "barcode": "920013", + "barcode": [ + "920013" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -1043,12 +1089,14 @@ "patron" ], "last_name": "Folliero", - "phone": "+41328619930", + "home_phone": "+41328619930", "postal_code": "7106", "street": "Quadra 93", "patron": { "expiration_date": "2023-10-07", - "barcode": "920014", + "barcode": [ + "920014" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -1068,12 +1116,14 @@ "patron" ], "last_name": "Bellucci", - "phone": "+41313966062", + "home_phone": "+41313966062", "postal_code": "6987", "street": "Via Camischolas sura 63", "patron": { "expiration_date": "2023-10-07", - "barcode": "920015", + "barcode": [ + "920015" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -1093,12 +1143,14 @@ "patron" ], "last_name": "Colombo", - "phone": "+41274865229", + "home_phone": "+41274865229", "postal_code": "6500", "street": "Lungolago 118", "patron": { "expiration_date": "2023-10-07", - "barcode": "920016", + "barcode": [ + "920016" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -1118,12 +1170,14 @@ "patron" ], "last_name": "de Baskerville", - "phone": "+41324993585", + "home_phone": "+41324993585", "postal_code": "6073", "street": "Ranftweg 1", "patron": { "expiration_date": "2023-10-07", - "barcode": "999999", + "barcode": [ + "999999" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/5" @@ -1150,7 +1204,7 @@ } ], "password": "123456", - "phone": "+39324993599", + "home_phone": "+39324993599", "postal_code": "11101", "street": "Central Park" }, @@ -1171,7 +1225,7 @@ } ], "password": "123456", - "phone": "+39324993596", + "home_phone": "+39324993596", "postal_code": "11102", "street": "Illogical street, 44" }, @@ -1187,12 +1241,14 @@ ], "last_name": "Kirk", "password": "123456", - "phone": "+41324883598", + "home_phone": "+41324883598", "postal_code": "4242", "street": "Galaxy street", "patron": { "expiration_date": "2023-10-07", - "barcode": "cypress-1", + "barcode": [ + "cypress-1" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/7" @@ -1213,12 +1269,14 @@ ], "last_name": "Uhura", "password": "123456", - "phone": "+41324883123", + "home_phone": "+41324883123", "postal_code": "4242", "street": "Milky Way street", "patron": { "expiration_date": "2023-10-07", - "barcode": "cypress-2", + "barcode": [ + "cypress-2" + ], "keep_history": false, "type": { "$ref": "https://ils.rero.ch/api/patron_types/7" diff --git a/pyproject.toml b/pyproject.toml index e2285308a8..c9da3fac55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ redisbeat = "*" jsonpickle = ">=1.4.1" ciso8601 = "*" # TODO: to be removed when the thumbnail will be refactored -invenio-userprofiles = {git = "https://github.com/rero/invenio-userprofiles.git", tag = "invenio-3.4"} +invenio-userprofiles = {git = "https://github.com/rero/invenio-userprofiles.git", tag = "v1.2.1-rero1.0"} ## Additionnal constraints on python modules flask-wiki = {git = "https://github.com/rero/flask-wiki.git"} diff --git a/rero_ils/config.py b/rero_ils/config.py index 5dec88095b..90862d15ac 100644 --- a/rero_ils/config.py +++ b/rero_ils/config.py @@ -99,6 +99,8 @@ from .modules.permissions import record_permission_factory from .modules.templates.api import Template from .modules.templates.permissions import TemplatePermission +from .modules.users.api import get_profile_countries, \ + get_readonly_profile_fields from .modules.vendors.api import Vendor from .modules.vendors.permissions import VendorPermission from .permissions import librarian_delete_permission_factory, \ @@ -228,6 +230,9 @@ def _(x): ACCOUNTS_USERINFO_HEADERS = False # Disable User Profiles USERPROFILES = True +USERPROFILES_COUNTRIES = get_profile_countries +USERPROFILES_DEFAULT_COUNTRY = 'sz' +USERPROFILES_READONLY_FIELDS = get_readonly_profile_fields # Custom login view ACCOUNTS_REST_AUTH_VIEWS = { @@ -810,7 +815,7 @@ def _(x): }, list_route='/patrons/', record_loaders={ - 'application/json': lambda: Patron(request.get_json()), + 'application/json': lambda: Patron.load(request.get_json()), }, record_class='rero_ils.modules.patrons.api:Patron', item_route=('/patrons/. """API for manipulating patrons.""" +from copy import deepcopy from datetime import datetime from functools import partial @@ -24,7 +25,6 @@ from flask_babelex import gettext as _ from flask_login import current_user from invenio_circulation.proxies import current_circulation -from invenio_db import db from werkzeug.local import LocalProxy from .models import PatronIdentifier, PatronMetadata @@ -36,8 +36,9 @@ from ..organisations.api import Organisation from ..patron_transactions.api import PatronTransaction from ..providers import Provider +from ..users.api import User from ..utils import extracted_data_from_ref, get_patron_from_arguments, \ - get_ref_for_pid, trim_barcode_for_record + get_ref_for_pid, trim_patron_barcode_for_record from ...utils import create_user_from_data _datastore = LocalProxy(lambda: current_app.extensions['security'].datastore) @@ -106,12 +107,6 @@ class Patron(IlsRecord): provider = PatronProvider model_cls = PatronMetadata - # field list to be in sync - profile_fields = [ - 'first_name', 'last_name', 'street', 'postal_code', - 'city', 'birth_date', 'username', 'phone', 'keep_history' - ] - available_roles = [ROLE_SYSTEM_LIBRARIAN, ROLE_LIBRARIAN, ROLE_PATRON] def _validate(self, **kwargs): @@ -200,30 +195,16 @@ def create(cls, data, id_=None, delete_pid=False, :param email_notification - send a reset password link to the user """ # remove spaces - data = trim_barcode_for_record(data=data) - # synchronize the rero id user profile data - user = cls._get_user_by_user_id(data.get('user_id')) - data = cls.merge_data_from_profile(data, user.profile) - try: - record = super().create( + data = trim_patron_barcode_for_record(data=data) + record = super().create( data, id_, delete_pid, dbcommit, reindex, **kwargs) - record._update_roles() - except Exception as err: - db.session.rollback() - raise err - # TODO: send reset password instruction when a librarian create a user + record._update_roles() return record def update(self, data, dbcommit=False, reindex=False): """Update data for record.""" # remove spaces - data = trim_barcode_for_record(data=data) - data = dict(self, **data) - - # synchronize the rero id user profile data - user = self._get_user_by_user_id(data.get('user_id')) - data = self.merge_data_from_profile(data, user.profile) - + data = trim_patron_barcode_for_record(data=data) super().update(data, dbcommit, reindex) self._update_roles() return self @@ -235,54 +216,29 @@ def delete(self, force=False, delindex=False): return self @classmethod - def merge_data_from_profile(cls, data, profile): - """Get the profile informations and inject it. - - TODO: move this to the indexing time. - """ - # retrieve the user - for field in cls.profile_fields: - # date field requires conversion - if field == 'birth_date': - data[field] = getattr( - profile, field).strftime('%Y-%m-%d') - elif field == 'keep_history': - if 'patron' in data.get('roles', []): - new_keep_history = getattr(profile, field) - data.setdefault('patron', {})['keep_history'] = new_keep_history - else: - value = getattr(profile, field) - if value not in [None, '']: - data[field] = value - # update the email - if profile.user.email != data.get('email'): - # the email is not defined or removed in the user profile - if not profile.user.email: - try: - del data['email'] - except KeyError: - pass - else: - # the email has been updated in the user profile - data['email'] = profile.user.email - return data + def load(cls, data): + """Load the data and remove the user data.""" + return cls(cls.removeUserData(data)) @classmethod - def update_from_profile(cls, data, profile): - """Update the current record with the user profile data. + def removeUserData(cls, data): + """Remove the user data.""" + data = deepcopy(data) + profile_fields = User.profile_fields + ['username', 'email'] + for field in profile_fields: + try: + del data[field] + except KeyError: + pass + return data - :param profile - the rero user profile - """ - # retrieve the user - patron = Patron.get_patron_by_user(profile.user) - if patron: - cls.merge_data_from_profile(dict(patron), profile) - super().update(dict(patron), True, True) - # TODO: do it at the profile changes - # anonymize user loans if keep_history is changed - # if old_keep_history and not new_keep_history: - # from ..loans.api import anonymize_loans - # anonymize_loans(patron_pid=patron.pid, dbcommit=True, reindex=True) + def dumps(self, **kwargs): + """Return pure Python dictionary with record metadata.""" + dump = super().dumps(**kwargs) + user = User.get_by_id(self['user_id']) + user_info = user.dumpsMetadata() + dump.update(user_info) + return dump @classmethod def _get_user_by_user_id(cls, user_id): @@ -476,20 +432,6 @@ def remove_role(self, role_name): _datastore.remove_role_from_user(self.user, role) _datastore.commit() - @property - def initial(self): - """Return the initials of the patron first name.""" - initial = '' - firsts = self['first_name'].split(' ') - for first in firsts: - initial += first[0] - lasts = self['last_name'].split(' ') - for last in lasts: - if last[0].isupper(): - initial += last[0] - - return initial - @property def patron(self): """Patron property shorcut.""" @@ -498,9 +440,10 @@ def patron(self): @property def formatted_name(self): """Return the best possible human readable patron name.""" + profile = self.user.profile name_parts = [ - self.get('last_name', '').strip(), - self.get('first_name', '').strip() + profile.last_name.strip(), + profile.first_name.strip() ] name_parts = [part for part in name_parts if part] # remove empty part return ', '.join(name_parts) diff --git a/rero_ils/modules/patrons/cli.py b/rero_ils/modules/patrons/cli.py index 7166b7196c..5497356f33 100644 --- a/rero_ils/modules/patrons/cli.py +++ b/rero_ils/modules/patrons/cli.py @@ -84,7 +84,7 @@ def import_users(infile, append, verbose, password, lazy, dont_stop_on_error, ), fg='yellow') if password: patron_data.pop('password', None) - # do nothing if the patron alredy exists + # do nothing if the patron already exists patron = Patron.get_patron_by_username(username) if patron: click.secho('{count: <8} Patron already exist: {username}'.format( @@ -122,6 +122,7 @@ def import_users(infile, append, verbose, password, lazy, dont_stop_on_error, patron.reindex() pids.append(patron.pid) except Exception as err: + raise(err) error_records.append(data) click.secho( '{count: <8} User create error: {err}'.format( diff --git a/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json b/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json index 011bb09a45..06c3dadf8e 100644 --- a/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json +++ b/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json @@ -6,10 +6,13 @@ "additionalProperties": false, "propertiesOrder": [ "user_id", + "second_address", "roles", "patron", "libraries", - "notes" + "notes", + "source", + "local_code" ], "required": [ "$schema", @@ -29,8 +32,20 @@ "title": "Patron ID", "type": "string" }, + "source": { + "title": "Source", + "description": "Source if the record has been loaded in a batch.", + "type": "string", + "minLength": 2 + }, + "local_code": { + "title": "Local code", + "description": "Code used to classify users, for instance for statistics.", + "type": "string", + "minLength": 2 + }, "user_id": { - "title": "Personal Informations", + "title": "Personal informations", "description": "", "type": "number", "form": { @@ -42,106 +57,59 @@ } } }, - "first_name": { - "title": "First name", - "type": "string", - "minLength": 2, - "form": { - "focus": true - } - }, - "last_name": { - "title": "Last name", - "type": "string", - "minLength": 2 - }, - "birth_date": { - "title": "Date of birth", - "type": "string", - "format": "date", - "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "form": { - "validation": { - "messages": { - "patternMessage": "Should be in the following format: 2022-12-31 (YYYY-MM-DD)." + "second_address": { + "title": "Second address", + "type": "object", + "additionalProperties": false, + "propertiesOrder": [ + "street", + "postal_code", + "city", + "country" + ], + "properties": { + "street": { + "title": "Street", + "description": "Street and number of the address.", + "type": "string", + "minLength": 1, + "form": { + "templateOptions": { + "itemCssClass": "col-lg-12" + } } }, - "placeholder": "Example: 1985-12-29" - } - }, - "email": { - "title": "Email", - "type": "string", - "format": "email", - "pattern": "^.*@.*\\..+$", - "minLength": 6, - "form": { - "expressionProperties": { - "templateOptions.required": "field.parent.model.roles.some(v => (v === 'librarian' || v === 'system_librarian')) || (field.parent.model.patron.communication_channel === 'email' && !field.parent.model.patron.additional_communication_email)" - }, - "validation": { - "validators": { - "valueAlreadyExists": { - "term": "email", - "remoteRecordType": "patrons/count" + "postal_code": { + "title": "Postal code", + "type": "string", + "minLength": 1, + "form": { + "templateOptions": { + "itemCssClass": "col-lg-6" } - }, - "messages": { - "patternMessage": "The email is not valid.", - "alreadyTakenMessage": "This email is already taken." } - } - } - }, - "username": { - "title": "Username", - "description": "Login username for the web interface.", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9-_]{2}[a-zA-Z0-9-_]*$", - "minLength": 3, - "form": { - "validation": { - "validators": { - "valueAlreadyExists": { - "term": "username", - "remoteRecordType": "patrons/count" + }, + "city": { + "title": "City", + "type": "string", + "minLength": 1, + "form": { + "templateOptions": { + "itemCssClass": "col-lg-6" } - }, - "messages": { - "patternMessage": "Username must start with a letter, be at least three characters long and only contain alphanumeric characters, dashes and underscores.", - "alreadyTakenMessage": "This username is already taken." } + }, + "country": { + "$ref": "https://ils.rero.ch/schemas/common/countries-v0.0.1.json#/country" } - } - }, - "street": { - "title": "Street", - "description": "Street and number of the address.", - "type": "string", - "minLength": 1 - }, - "postal_code": { - "title": "Postal code", - "type": "string", - "minLength": 1 - }, - "city": { - "title": "City", - "type": "string", - "minLength": 1 - }, - "phone": { - "title": "Phone number", - "description": "Phone number with the international prefix, without spaces.", - "type": "string", - "pattern": "^\\+[0-9]*$", + }, "form": { - "validation": { - "messages": { - "patternMessage": "Phone number with the international prefix, without spaces, ie +41221234567." - } - }, - "placeholder": "Example: +41791231212" + "templateOptions": { + "wrappers": [ + "card" + ], + "containerCssClass": "row" + } } }, "patron": { @@ -163,7 +131,6 @@ "communication_language", "expiration_date", "libraries", - "keep_history", "blocked", "blocked_note" ], @@ -188,16 +155,25 @@ } }, "barcode": { - "title": "Patron's barcode or card number", - "type": "string", - "minLength": 6, - "form": { - "validation": { - "validators": { - "valueAlreadyExists": {} - }, - "messages": { - "alreadyTakenMessage": "The barcode is already taken." + "title": "Patron's barcodes or cards number", + "type": "array", + "minItems": 1, + "maxItems": 2, + "uniqueItems": true, + "items": { + "title": "Patron's barcode or card number", + "type": "string", + "minLength": 6, + "form": { + "validation": { + "validators": { + "valueAlreadyExists": { + "term": "barcode" + } + }, + "messages": { + "alreadyTakenMessage": "The barcode is already taken." + } } } } @@ -469,13 +445,19 @@ } }, "form": { - "fieldMap": "roles" + "fieldMap": "roles", + "templateOptions": { + "wrappers": [ + "card" + ] + } } }, "notes": { "title": "Notes", "description": "The public note is visible for the patron in his/her account.", "type": "array", + "minItems": 0, "items": { "type": "object", "additionalProperties": false, diff --git a/rero_ils/modules/patrons/listener.py b/rero_ils/modules/patrons/listener.py index 58f02dcb03..b24d4a999d 100644 --- a/rero_ils/modules/patrons/listener.py +++ b/rero_ils/modules/patrons/listener.py @@ -43,7 +43,6 @@ def enrich_patron_data(sender, json=None, record=None, index=None, 'pid': org_pid } - def create_subscription_patron_transaction(sender, record=None, **kwargs): """This method check the patron to know if a subscription is requested. @@ -76,4 +75,15 @@ def update_from_profile(sender, profile=None, **kwargs): :param profile - the rero user profile """ - Patron.update_from_profile(profile) + patron = Patron.get_patron_by_user(profile.user) + if patron: + old_keep_history = patron.get('patron', {}).get('keep_history') + patron.reindex() + from ..loans.api import anonymize_loans + new_keep_history = profile.keep_history + if old_keep_history and not new_keep_history: + anonymize_loans( + patron_data=patron, + patron_pid=patron.get('pid'), + dbcommit=True, + reindex=True) diff --git a/rero_ils/modules/patrons/mappings/v7/patrons/patron-v0.0.1.json b/rero_ils/modules/patrons/mappings/v7/patrons/patron-v0.0.1.json index 2faacd1860..9bea64b029 100644 --- a/rero_ils/modules/patrons/mappings/v7/patrons/patron-v0.0.1.json +++ b/rero_ils/modules/patrons/mappings/v7/patrons/patron-v0.0.1.json @@ -61,7 +61,35 @@ "city": { "type": "text" }, - "phone": { + "country": { + "type": "text" + }, + "second_address": { + "properties": { + "street": { + "type": "text" + }, + "postal_code": { + "type": "keyword" + }, + "city": { + "type": "text" + }, + "country": { + "type": "text" + } + } + }, + "home_phone": { + "type": "keyword" + }, + "business_phone": { + "type": "keyword" + }, + "mobile_phone": { + "type": "keyword" + }, + "other_phone": { "type": "keyword" }, "barcode": { diff --git a/rero_ils/modules/patrons/tasks.py b/rero_ils/modules/patrons/tasks.py index 58fac6fd24..ed2db2ab46 100644 --- a/rero_ils/modules/patrons/tasks.py +++ b/rero_ils/modules/patrons/tasks.py @@ -55,7 +55,7 @@ def is_obsolete(subscription, end_date=None): # NOTE : this update will trigger the listener # `create_subscription_patron_transaction`. This listener will # create a new subscription if needed - patron.update(patron.dumps(), dbcommit=True, reindex=True) + patron.update(Patron.removeUserData(patron.dumps()), dbcommit=True, reindex=True) def check_patron_types_and_add_subscriptions(): diff --git a/rero_ils/modules/patrons/templates/rero_ils/_patron_profile_personal.html b/rero_ils/modules/patrons/templates/rero_ils/_patron_profile_personal.html index d12228bde2..543c8a0aad 100644 --- a/rero_ils/modules/patrons/templates/rero_ils/_patron_profile_personal.html +++ b/rero_ils/modules/patrons/templates/rero_ils/_patron_profile_personal.html @@ -96,7 +96,7 @@
- {{ record.patron.barcode }} + {{ record.patron.barcode[0] }}
{% endif %} @@ -117,7 +117,7 @@ {% endif %} - {% if record.patron.barcode %} + {% if record.patron %}
{{_('Keep history')}}: diff --git a/rero_ils/modules/patrons/templates/rero_ils/patron_profile.html b/rero_ils/modules/patrons/templates/rero_ils/patron_profile.html index 16629508a2..8841ea2746 100644 --- a/rero_ils/modules/patrons/templates/rero_ils/patron_profile.html +++ b/rero_ils/modules/patrons/templates/rero_ils/patron_profile.html @@ -59,7 +59,7 @@ {{ fees.open.total_amount | format_currency(fees.open.currency) }} - {% if record.patron.keep_history %} + {% if record.dumps().keep_history %}