Skip to content

Commit

Permalink
oauth: add patron pid
Browse files Browse the repository at this point in the history
* Adds `patron_info` with patron pid for institutions to scope.
* Closes #3778.

Co-Authored-by: Peter Weber <[email protected]>
  • Loading branch information
rerowep committed Dec 4, 2024
1 parent cea2d9f commit c5277af
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 67 deletions.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,12 @@ vendors = "rero_ils.modules.vendors.jsonschemas"
files = "rero_ils.modules.files.jsonschemas"

[tool.poetry.plugins."invenio_oauth2server.scopes"]
fullname = "rero_ils.oauth.scopes:fullname"
birthdate = "rero_ils.oauth.scopes:birthdate"
expiration_date = "rero_ils.oauth.scopes:expiration_date"
fullname = "rero_ils.oauth.scopes:fullname"
institution = "rero_ils.oauth.scopes:institution"
patron_type = "rero_ils.oauth.scopes:patron_type"
# deprecated scopes
patron_types = "rero_ils.oauth.scopes:patron_types"

[tool.poetry.plugins."invenio_pidstore.fetchers"]
Expand Down
3 changes: 3 additions & 0 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3613,6 +3613,9 @@ def _(x):
"bf:Place": "places",
}

# Change institution code for /info
RERO_ILS_APP_INSTITUTION_CODE_TRANSFORMATION = {"nj": "rbnj"}

# The absolute path to put the agent synchronization logs, default is the
# instance path
# RERO_ILS_MEF_SYNC_LOG_DIR = "/var/logs/reroils"
Expand Down
115 changes: 59 additions & 56 deletions rero_ils/modules/patrons/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2019-2023 RERO
# Copyright (C) 2024 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
Expand All @@ -19,7 +19,6 @@

from __future__ import absolute_import, print_function

import copy
import datetime
import re

Expand Down Expand Up @@ -321,78 +320,82 @@ def info():
"""Get patron info."""
token_scopes = flask_request.oauth.access_token.scopes

def get_main_patron(patrons):
"""Return the main patron.
:param patrons: List of patrons.
:returns: The main patron.
"""
# TODO: Find a way to determine which is the main patron.
return patrons[0]

def get_institution_code(institution):
"""Get the institution code for a given institution.
Special transformation for `nj`.
:param institution: Institution object.
:returns: Code for the institution.
"""
# TODO: make this non rero specific using a configuration
return institution["code"] if institution["code"] != "nj" else "rbnj"

user = User.get_record(current_user.id).dumps_metadata()
code = institution["code"]
if changed_code := current_app.config.get(
"RERO_ILS_APP_INSTITUTION_CODE_TRANSFORMATION", {}
).get(code):
return changed_code
return code

# Process for all patrons
patrons = copy.deepcopy(current_patrons)
for patron in patrons:
patron["institution"] = patron.organisation
patron["patron"]["type"] = PatronType.get_record_by_pid(
extracted_data_from_ref(patron["patron"]["type"]["$ref"])
)

# Birthdate
data = {}
birthdate = current_user.user_profile.get("birth_date")
if "birthdate" in token_scopes and birthdate:
data["birthdate"] = birthdate
current_user.user_profile.get("last_name", "").strip(),
data = {"user_id": current_user.id}
# Full name
name_parts = [
current_user.user_profile.get("last_name", "").strip(),
current_user.user_profile.get("first_name", "").strip(),
]
fullname = ", ".join(filter(None, name_parts))
if "fullname" in token_scopes and fullname:
if fullname and "fullname" in token_scopes:
data["fullname"] = fullname
birthdate = current_user.user_profile.get("birth_date")
# Birthdate
if birthdate and "birthdate" in token_scopes:
data["birthdate"] = birthdate

# No patrons found for user
if not patrons:
return jsonify(data)

# Get the main patron
patron = get_main_patron(patrons)
# Barcode
if patron.get("patron", {}).get("barcode"):
data["barcode"] = patron["patron"]["barcode"][0]
# Patron types
if "patron_types" in token_scopes:
patron_types = []
for patron in patrons:
info = {}
patron_type_code = patron.get("patron", {}).get("type", {}).get("code")
if patron_type_code:
patrons = current_patrons
if len(patrons) > 0:
patron = patrons[0]
# Barcode
if patron.get("patron", {}).get("barcode"):
data["barcode"] = patron["patron"]["barcode"][0]
# Patron
patron_pids = []
patron_types = []
patron_infos = {}
for patron in patrons:
patron_pids.append(patron.pid)
patron_type = PatronType.get_record_by_pid(
extracted_data_from_ref(patron["patron"]["type"]["$ref"])
)
patron_type_code = patron_type.get("code")
institution = get_institution_code(patron.organisation)
expiration_date = patron.get("patron", {}).get("expiration_date")

# old list (patron_types)
if "patron_types" in token_scopes:
info = {"patron_pid": patron.pid}
if patron_type_code and "patron_type" in token_scopes:
info["patron_type"] = patron_type_code
if patron.get("institution"):
info["institution"] = get_institution_code(patron["institution"])
if patron.get("patron", {}).get("expiration_date"):
if institution and "institution" in token_scopes:
info["institution"] = institution
if expiration_date and "expiration_date" in token_scopes:
info["expiration_date"] = datetime.datetime.strptime(
patron["patron"]["expiration_date"], "%Y-%m-%d"
expiration_date, "%Y-%m-%d"
).isoformat()
if info:
patron_types.append(info)
if patron_types:
data["patron_types"] = patron_types

patron_types.append(info)

# new dict (patron_info)
patron_info = {"patron_pid": patron.pid}
if institution and "institution" in token_scopes:
patron_info["institution"] = institution
if patron_type_code and "patron_type" in token_scopes:
patron_info["patron_type"] = patron_type_code
if expiration_date and "expiration_date" in token_scopes:
patron_info["expiration_date"] = datetime.datetime.strptime(
expiration_date, "%Y-%m-%d"
).isoformat()
patron_infos[institution] = patron_info

if patron_types:
data["patron_types"] = patron_types
if patron_infos:
data["patron_info"] = patron_infos
return jsonify(data)


Expand Down
8 changes: 5 additions & 3 deletions rero_ils/oauth/scopes.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2021 RERO
# Copyright (C) 2024 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
Expand All @@ -21,7 +21,9 @@

fullname = Scope("fullname", help_text="Full name", group="User")
birthdate = Scope("birthdate", help_text="Birthdate", group="User")
institution = Scope("institution", help_text="Institution", group="User")
expiration_date = Scope("expiration_date", help_text="Expiration date", group="User")
institution = Scope("institution", help_text="Institution", group="User")
patron_type = Scope("patron_type", help_text="Patron type", group="User")
patron_types = Scope("patron_types", help_text="Patron types", group="User")
patron_types = Scope(
"patron_types", help_text="Patron types (deprecated)", group="User"
)
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,12 @@ def run(self):
'invenio_oauth2server.scopes': [
'fullname = rero_ils.oauth.scopes:fullname',
'birthdate = rero_ils.oauth.scopes:birthdate',
'institution = rero_ils.oauth.scopes:institution',
'patron_info = rero_ils.oauth.scopes:patron_info',
# deprecated scopes
'expiration_date = rero_ils.oauth.scopes:expiration_date',
'institution = rero_ils.oauth.scopes:institution',
'patron_type = rero_ils.oauth.scopes:patron_type',
'patron_types = rero_ils.oauth.scopes:patron_types'
'patron_types = rero_ils.oauth.scopes:patron_types',
]
},
classifiers=[
Expand Down
31 changes: 26 additions & 5 deletions tests/api/patrons/test_patrons_rest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2019 RERO
# Copyright (C) 2024 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
Expand All @@ -23,6 +23,7 @@

import mock
from flask import url_for
from flask_login import current_user
from invenio_accounts.testutils import login_user_via_session
from invenio_db import db
from invenio_oauth2server.models import Client, Token
Expand Down Expand Up @@ -545,31 +546,51 @@ def test_patron_info(app, client, patron_martigny, librarian_martigny):
url_for("api_patrons.info", access_token=no_scope_token.access_token)
)
assert res.status_code == 200
assert res.json == {"barcode": patron_martigny["patron"]["barcode"].pop()}
barcode = patron_martigny["patron"]["barcode"].pop()
assert res.json == {
"barcode": barcode,
"user_id": patron_martigny.user.id,
"patron_info": {"ORG1": {"patron_pid": patron_martigny.pid}},
}

# full information with all scopes
res = client.get(url_for("api_patrons.info", access_token=token.access_token))
assert res.status_code == 200
assert res.json == {
"barcode": "4098124352",
"barcode": barcode,
"user_id": patron_martigny.user.id,
"birthdate": "1947-06-07",
"fullname": "Roduit, Louis",
"patron_types": [
{
"expiration_date": patron_martigny["patron"]["expiration_date"]
+ "T00:00:00",
"institution": "org1",
"institution": "ORG1",
"patron_pid": patron_martigny.pid,
"patron_type": "patron-code",
}
],
"patron_info": {
"ORG1": {
"expiration_date": patron_martigny["patron"]["expiration_date"]
+ "T00:00:00",
"institution": "ORG1",
"patron_pid": patron_martigny.pid,
"patron_type": "patron-code",
}
},
}

# librarian information with all scopes
res = client.get(
url_for("api_patrons.info", access_token=librarian_token.access_token)
)
assert res.status_code == 200
assert res.json == {"birthdate": "1965-02-07", "fullname": "Pedronni, Marie"}
assert res.json == {
"birthdate": "1965-02-07",
"fullname": "Pedronni, Marie",
"user_id": librarian_martigny.user.id,
}


def test_patrons_search(client, librarian_martigny):
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ def app_config(app_config):
},
}
app_config["INDEXER_DEFAULT_INDEX"] = "records-record-v1.0.0"
app_config["RERO_ILS_APP_INSTITUTION_CODE_TRANSFORMATION"] = {"org1": "ORG1"}
return app_config


Expand Down

0 comments on commit c5277af

Please sign in to comment.