Skip to content

Commit

Permalink
oauth: adds user_id and patron_info
Browse files Browse the repository at this point in the history
* Adds `user_id`.
* Adds `patron_info` scope with patron pid, patron type, institution
  and expiration_date.
* Marks `patron_types` as deprecated.
* Closes #3778.

Co-Authored-by: Peter Weber <[email protected]>
  • Loading branch information
rerowep committed Dec 5, 2024
1 parent 41504d0 commit 605a258
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 73 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
113 changes: 50 additions & 63 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,66 @@ 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()

# 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
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 patrons:
patron = patrons[0]
# Barcode
if patron.get("patron", {}).get("barcode"):
data["barcode"] = patron["patron"]["barcode"][0]
# Patron
patron_types = []
patron_infos = {}
for patron in patrons:
patron_type = PatronType.get_record_by_pid(
extracted_data_from_ref(patron["patron"]["type"]["$ref"])
)
patron_type_code = patron_type.get("code")
institution = patron.organisation["code"]
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',
'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'
# deprecated scopes
'patron_types = rero_ils.oauth.scopes:patron_types',
]
},
classifiers=[
Expand Down
28 changes: 24 additions & 4 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 Down Expand Up @@ -545,31 +545,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",
"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

0 comments on commit 605a258

Please sign in to comment.