invenio-app-ils 4.5.0__py2.py3-none-any.whl → 5.0.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- invenio_app_ils/__init__.py +1 -1
- invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
- invenio_app_ils/circulation/api.py +9 -5
- invenio_app_ils/circulation/config.py +1 -0
- invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +1 -1
- invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +2 -2
- invenio_app_ils/circulation/notifications/api.py +9 -1
- invenio_app_ils/circulation/notifications/messages.py +2 -1
- invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
- invenio_app_ils/cli.py +92 -64
- invenio_app_ils/config.py +10 -2
- invenio_app_ils/documents/loaders/jsonschemas/document.py +1 -1
- invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +2 -2
- invenio_app_ils/patrons/anonymization.py +0 -4
- invenio_app_ils/series/loaders/jsonschemas/series.py +1 -1
- invenio_app_ils/webpack.py +27 -0
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/METADATA +56 -43
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/RECORD +24 -112
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/entry_points.txt +3 -0
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/top_level.txt +0 -1
- invenio_app_ils/notifications/admin.py +0 -57
- tests/__init__.py +0 -8
- tests/api/__init__.py +0 -8
- tests/api/acquisition/__init__.py +0 -8
- tests/api/acquisition/test_acq_orders_crud.py +0 -99
- tests/api/acquisition/test_acq_orders_permissions.py +0 -104
- tests/api/acquisition/test_acq_providers_permissions.py +0 -83
- tests/api/circulation/__init__.py +0 -8
- tests/api/circulation/test_expired_loans.py +0 -88
- tests/api/circulation/test_loan_bulk_extend.py +0 -169
- tests/api/circulation/test_loan_checkout.py +0 -422
- tests/api/circulation/test_loan_default_durations.py +0 -43
- tests/api/circulation/test_loan_document_resolver.py +0 -18
- tests/api/circulation/test_loan_extend.py +0 -122
- tests/api/circulation/test_loan_item_permissions.py +0 -135
- tests/api/circulation/test_loan_item_resolver.py +0 -26
- tests/api/circulation/test_loan_list_permissions.py +0 -98
- tests/api/circulation/test_loan_patron_resolver.py +0 -38
- tests/api/circulation/test_loan_request.py +0 -338
- tests/api/circulation/test_loan_update.py +0 -150
- tests/api/circulation/test_notifications.py +0 -221
- tests/api/conftest.py +0 -227
- tests/api/document_requests/__init__.py +0 -8
- tests/api/document_requests/test_document_requests.py +0 -131
- tests/api/document_requests/test_document_requests_permissions.py +0 -159
- tests/api/document_requests/test_notifications_filter.py +0 -35
- tests/api/ill/__init__.py +0 -8
- tests/api/ill/test_ill_brw_crud.py +0 -74
- tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -198
- tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -280
- tests/api/ill/test_ill_brw_reqs_permissions.py +0 -163
- tests/api/ill/test_ill_providers_permissions.py +0 -82
- tests/api/ils/__init__.py +0 -8
- tests/api/ils/documents/__init__.py +0 -8
- tests/api/ils/documents/test_document_crud.py +0 -57
- tests/api/ils/documents/test_document_permissions.py +0 -100
- tests/api/ils/documents/test_document_resolvers.py +0 -35
- tests/api/ils/eitems/__init__.py +0 -8
- tests/api/ils/eitems/test_eitems_crud.py +0 -42
- tests/api/ils/eitems/test_eitems_permissions.py +0 -85
- tests/api/ils/eitems/test_files.py +0 -162
- tests/api/ils/items/__init__.py +0 -8
- tests/api/ils/items/test_apis.py +0 -43
- tests/api/ils/items/test_items_crud.py +0 -99
- tests/api/ils/items/test_items_permissions.py +0 -107
- tests/api/ils/records_relations/__init__.py +0 -8
- tests/api/ils/records_relations/helpers.py +0 -115
- tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
- tests/api/ils/records_relations/test_records_relations_sequence.py +0 -294
- tests/api/ils/records_relations/test_records_relations_siblings.py +0 -751
- tests/api/ils/series/__init__.py +0 -8
- tests/api/ils/series/test_series_permissions.py +0 -95
- tests/api/ils/test_anonymization.py +0 -181
- tests/api/ils/test_apis.py +0 -76
- tests/api/ils/test_closures.py +0 -353
- tests/api/ils/test_errors.py +0 -125
- tests/api/ils/test_facets.py +0 -88
- tests/api/ils/test_internal_locations.py +0 -96
- tests/api/ils/test_loaders.py +0 -51
- tests/api/ils/test_metadata_extensions.py +0 -206
- tests/api/ils/test_notifications.py +0 -173
- tests/api/ils/test_notifications_mails.py +0 -37
- tests/api/ils/test_notifications_permissions.py +0 -55
- tests/api/ils/test_patrons.py +0 -102
- tests/api/ils/test_record_delete.py +0 -42
- tests/api/ils/test_record_permissions.py +0 -132
- tests/api/ils/test_resolvers.py +0 -205
- tests/api/ils/test_stats.py +0 -142
- tests/api/ils/test_tasks.py +0 -209
- tests/api/ils/test_vocabularies.py +0 -35
- tests/conftest.py +0 -221
- tests/data/acq_orders.json +0 -110
- tests/data/acq_providers.json +0 -12
- tests/data/document_requests.json +0 -77
- tests/data/documents.json +0 -238
- tests/data/eitems.json +0 -71
- tests/data/ill_borrowing_requests.json +0 -77
- tests/data/ill_providers.json +0 -12
- tests/data/internal_locations.json +0 -22
- tests/data/items.json +0 -304
- tests/data/loans.json +0 -133
- tests/data/loans_most_loaned.json +0 -128
- tests/data/locations.json +0 -26
- tests/data/series.json +0 -33
- tests/helpers.py +0 -107
- tests/templates/notifications/title_body.html +0 -8
- tests/templates/notifications/title_body_html.html +0 -13
- tests/templates/notifications/title_body_html_ctx.html +0 -13
- tests/templates/notifications/title_only.html +0 -3
- tests/test_post_logout_redirect.py +0 -23
- tests/test_version.py +0 -17
- /tests/templates/notifications/blank.html → /invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/WHEEL +0 -0
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/AUTHORS.rst +0 -0
- {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2018-2019 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test record permissions."""
|
|
9
|
-
|
|
10
|
-
import uuid
|
|
11
|
-
|
|
12
|
-
import pytest
|
|
13
|
-
from flask_principal import RoleNeed, identity_loaded
|
|
14
|
-
from invenio_access.models import ActionRoles
|
|
15
|
-
from invenio_accounts.models import Role
|
|
16
|
-
from invenio_records.api import Record
|
|
17
|
-
from invenio_search.api import RecordsSearch
|
|
18
|
-
|
|
19
|
-
from invenio_app_ils.errors import UnauthorizedSearchError
|
|
20
|
-
from invenio_app_ils.records.permissions import RecordPermission, create_records_action
|
|
21
|
-
from invenio_app_ils.search_permissions import _filter_by_patron
|
|
22
|
-
from tests.helpers import user_login
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@pytest.mark.parametrize(
|
|
26
|
-
"patron_pid,qs,should_raise",
|
|
27
|
-
[
|
|
28
|
-
("1", None, False),
|
|
29
|
-
("1", "", False),
|
|
30
|
-
("1", "pid:1234", False),
|
|
31
|
-
("1", "patron_pid:2", True),
|
|
32
|
-
("1", "patron_pid: 2", True),
|
|
33
|
-
("1", "patron_pid: '2'", False),
|
|
34
|
-
("1", "pid:1234 AND patron_pid:2", True),
|
|
35
|
-
],
|
|
36
|
-
)
|
|
37
|
-
def test_filter_by_patron(app, patron_pid, qs, should_raise):
|
|
38
|
-
"""Test the function filter_by_patron."""
|
|
39
|
-
search = RecordsSearch()
|
|
40
|
-
if should_raise:
|
|
41
|
-
with pytest.raises(UnauthorizedSearchError):
|
|
42
|
-
_filter_by_patron(patron_pid, search, qs)
|
|
43
|
-
else:
|
|
44
|
-
_search, _qs = _filter_by_patron(patron_pid, search, qs)
|
|
45
|
-
term = _search.to_dict()["query"]["bool"]["filter"][0]["term"]
|
|
46
|
-
assert term == {"patron_pid": patron_pid}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_record_generic_access(client, db, users, with_access):
|
|
50
|
-
"""Test access control for records."""
|
|
51
|
-
|
|
52
|
-
tests = [
|
|
53
|
-
({"foo": "bar"}, "read", True),
|
|
54
|
-
({"foo": "bar"}, "update", False),
|
|
55
|
-
({"_access": {"read": [1]}}, "read", True),
|
|
56
|
-
({"_access": {"read": [2]}}, "read", False),
|
|
57
|
-
({"_access": {"read": ["records-readers"]}}, "read", True),
|
|
58
|
-
# permission for specific user to create
|
|
59
|
-
({"_access": {"update": [1]}}, "update", True),
|
|
60
|
-
# checks if the access works for different actions
|
|
61
|
-
({"_access": {"update": [1]}}, "create", False),
|
|
62
|
-
({"_access": {"delete": [1]}}, "update", False),
|
|
63
|
-
# delete access for user and librarian
|
|
64
|
-
({"_access": {"delete": [1, "librarian"]}}, "delete", True),
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
@identity_loaded.connect
|
|
68
|
-
def add_roles_to_identity(sender, identity):
|
|
69
|
-
"""Provide additional role to the user."""
|
|
70
|
-
roles = [RoleNeed("records-readers")]
|
|
71
|
-
identity.provides |= set(roles)
|
|
72
|
-
|
|
73
|
-
def login_and_test(username):
|
|
74
|
-
user = user_login(client, username, users)
|
|
75
|
-
# Create record
|
|
76
|
-
id = uuid.uuid4()
|
|
77
|
-
record = Record.create(access, id_=id)
|
|
78
|
-
factory = RecordPermission(record, action)
|
|
79
|
-
if user.has_role("admin"):
|
|
80
|
-
# super user can do EVERYTHING
|
|
81
|
-
assert factory.can()
|
|
82
|
-
elif user.has_role("librarian") and action != "delete":
|
|
83
|
-
# librarian should be able to update, create, and read everything
|
|
84
|
-
assert factory.can()
|
|
85
|
-
else:
|
|
86
|
-
assert factory.can() if is_allowed else not factory.can()
|
|
87
|
-
|
|
88
|
-
for access, action, is_allowed in tests:
|
|
89
|
-
# Test standard user
|
|
90
|
-
login_and_test("patron1")
|
|
91
|
-
# Test librarian access
|
|
92
|
-
login_and_test("librarian")
|
|
93
|
-
# Test superuser access
|
|
94
|
-
login_and_test("admin")
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@pytest.fixture()
|
|
98
|
-
def with_role_creator(db):
|
|
99
|
-
""" "Create a new role and assign action."""
|
|
100
|
-
role = Role(name="records-creators")
|
|
101
|
-
db.session.add(role)
|
|
102
|
-
db.session.commit()
|
|
103
|
-
# assign role to the action "create-records"
|
|
104
|
-
ar = ActionRoles.allow(create_records_action, role_id=role.id)
|
|
105
|
-
db.session.add(ar)
|
|
106
|
-
db.session.commit()
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def test_record_patron_create(client, db, users, with_role_creator):
|
|
110
|
-
"""Test patron create."""
|
|
111
|
-
|
|
112
|
-
tests = [
|
|
113
|
-
({"foo": "bar"}, "create", True),
|
|
114
|
-
({"foo": "bar"}, "update", False),
|
|
115
|
-
({"foo": "bar"}, "delete", False),
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
@identity_loaded.connect
|
|
119
|
-
def add_roles_to_identity(sender, identity):
|
|
120
|
-
"""Provide additional role to the user."""
|
|
121
|
-
roles = [RoleNeed("records-creators")]
|
|
122
|
-
identity.provides |= set(roles)
|
|
123
|
-
|
|
124
|
-
for access, action, is_allowed in tests:
|
|
125
|
-
# create role to be able to create records
|
|
126
|
-
user_login(client, "patron1", users)
|
|
127
|
-
|
|
128
|
-
id = uuid.uuid4()
|
|
129
|
-
record = Record.create(access, id_=id)
|
|
130
|
-
factory = RecordPermission(record, action)
|
|
131
|
-
|
|
132
|
-
assert factory.can() if is_allowed else not factory.can()
|
tests/api/ils/test_resolvers.py
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2020 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test Items resolvers."""
|
|
9
|
-
|
|
10
|
-
from invenio_circulation.pidstore.pids import CIRCULATION_LOAN_PID_TYPE
|
|
11
|
-
|
|
12
|
-
from invenio_app_ils.acquisition.api import ORDER_PID_TYPE
|
|
13
|
-
from invenio_app_ils.document_requests.api import DOCUMENT_REQUEST_PID_TYPE
|
|
14
|
-
from invenio_app_ils.documents.api import DOCUMENT_PID_TYPE, Document
|
|
15
|
-
from invenio_app_ils.documents.indexer import DocumentIndexer
|
|
16
|
-
from invenio_app_ils.eitems.api import EITEM_PID_TYPE, EItem
|
|
17
|
-
from invenio_app_ils.eitems.indexer import EItemIndexer
|
|
18
|
-
from invenio_app_ils.ill.api import BORROWING_REQUEST_PID_TYPE
|
|
19
|
-
from invenio_app_ils.internal_locations.indexer import InternalLocationIndexer
|
|
20
|
-
from invenio_app_ils.items.api import ITEM_PID_TYPE, Item
|
|
21
|
-
from invenio_app_ils.items.indexer import ItemIndexer
|
|
22
|
-
from invenio_app_ils.locations.api import LOCATION_PID_TYPE, Location
|
|
23
|
-
from invenio_app_ils.locations.indexer import LocationIndexer
|
|
24
|
-
from invenio_app_ils.patrons.api import PATRON_PID_TYPE, Patron
|
|
25
|
-
from invenio_app_ils.patrons.indexer import PatronIndexer
|
|
26
|
-
|
|
27
|
-
from invenio_app_ils.internal_locations.api import ( # isort:skip
|
|
28
|
-
INTERNAL_LOCATION_PID_TYPE,
|
|
29
|
-
InternalLocation,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_jsonresolvers(testdata, mocker):
|
|
34
|
-
"""Test that all records referencing a changed record are re-indexed."""
|
|
35
|
-
|
|
36
|
-
def _get_mock():
|
|
37
|
-
return mocker.patch("invenio_app_ils.indexer.ReferencedRecordsIndexer.index")
|
|
38
|
-
|
|
39
|
-
def _assert_origin(indexer, pid_type, pid_value):
|
|
40
|
-
"""Assert that origin is called and return all referenced."""
|
|
41
|
-
assert indexer.called
|
|
42
|
-
all_calls = indexer.call_args_list
|
|
43
|
-
first_call = all_calls[0]
|
|
44
|
-
indexed, referenced = first_call[0]
|
|
45
|
-
assert indexed["pid_type"] == pid_type
|
|
46
|
-
assert indexed["record"]["pid"] == pid_value
|
|
47
|
-
|
|
48
|
-
# call[0] is the list of arguments passed to the function
|
|
49
|
-
# call[0][0] is the origin record, call[0][1] is the referenced record
|
|
50
|
-
all_referenced = []
|
|
51
|
-
for call in all_calls:
|
|
52
|
-
origin, referenced = call[0]
|
|
53
|
-
all_referenced += referenced
|
|
54
|
-
return all_referenced
|
|
55
|
-
|
|
56
|
-
def _assert_contains(referenced, pid_type):
|
|
57
|
-
"""Assert that given PID is contained in one of the referenced."""
|
|
58
|
-
found = False
|
|
59
|
-
for r in referenced:
|
|
60
|
-
if r["pid_type"] == pid_type:
|
|
61
|
-
found = True
|
|
62
|
-
break
|
|
63
|
-
assert found
|
|
64
|
-
|
|
65
|
-
def test_on_document_update():
|
|
66
|
-
"""Test document resolvers."""
|
|
67
|
-
indexer = _get_mock()
|
|
68
|
-
|
|
69
|
-
pid = "docid-2"
|
|
70
|
-
document = Document.get_record_by_pid(pid)
|
|
71
|
-
DocumentIndexer().index(document)
|
|
72
|
-
|
|
73
|
-
referenced = _assert_origin(indexer, DOCUMENT_PID_TYPE, pid)
|
|
74
|
-
|
|
75
|
-
# should re-index document request
|
|
76
|
-
n_doc_req = 1 # from test data
|
|
77
|
-
_assert_contains(referenced, DOCUMENT_REQUEST_PID_TYPE)
|
|
78
|
-
|
|
79
|
-
# should re-index eitem
|
|
80
|
-
n_eitems = 1 # from test data
|
|
81
|
-
_assert_contains(referenced, EITEM_PID_TYPE)
|
|
82
|
-
|
|
83
|
-
# should re-index item
|
|
84
|
-
n_items = 1 # from test data
|
|
85
|
-
_assert_contains(referenced, ITEM_PID_TYPE)
|
|
86
|
-
|
|
87
|
-
# should re-index acq
|
|
88
|
-
n_acq = 1 # from test data
|
|
89
|
-
_assert_contains(referenced, ORDER_PID_TYPE)
|
|
90
|
-
|
|
91
|
-
# should re-index ill
|
|
92
|
-
n_ill = 1 # from test data
|
|
93
|
-
_assert_contains(referenced, BORROWING_REQUEST_PID_TYPE)
|
|
94
|
-
|
|
95
|
-
expected_total = n_doc_req + n_eitems + n_items + n_acq + n_ill
|
|
96
|
-
assert len(referenced) == expected_total
|
|
97
|
-
|
|
98
|
-
def test_on_eitem_update():
|
|
99
|
-
"""Test eitem resolvers."""
|
|
100
|
-
indexer = _get_mock()
|
|
101
|
-
|
|
102
|
-
pid = "eitemid-1"
|
|
103
|
-
eitem = EItem.get_record_by_pid(pid)
|
|
104
|
-
EItemIndexer().index(eitem)
|
|
105
|
-
|
|
106
|
-
referenced = _assert_origin(indexer, EITEM_PID_TYPE, pid)
|
|
107
|
-
|
|
108
|
-
# should re-index documents
|
|
109
|
-
n_documents = 1 # from test data
|
|
110
|
-
_assert_contains(referenced, DOCUMENT_PID_TYPE)
|
|
111
|
-
|
|
112
|
-
assert len(referenced) == n_documents
|
|
113
|
-
|
|
114
|
-
def test_on_internal_location_update():
|
|
115
|
-
"""Test internal location resolvers."""
|
|
116
|
-
indexer = _get_mock()
|
|
117
|
-
|
|
118
|
-
pid = "ilocid-2"
|
|
119
|
-
intloc = InternalLocation.get_record_by_pid(pid)
|
|
120
|
-
InternalLocationIndexer().index(intloc)
|
|
121
|
-
|
|
122
|
-
referenced = _assert_origin(indexer, INTERNAL_LOCATION_PID_TYPE, pid)
|
|
123
|
-
|
|
124
|
-
# should re-index items
|
|
125
|
-
n_items = 4 # from test data
|
|
126
|
-
_assert_contains(referenced, ITEM_PID_TYPE)
|
|
127
|
-
|
|
128
|
-
assert len(referenced) == n_items
|
|
129
|
-
|
|
130
|
-
def test_on_item_update():
|
|
131
|
-
"""Test item resolvers."""
|
|
132
|
-
indexer = _get_mock()
|
|
133
|
-
|
|
134
|
-
# item on loan
|
|
135
|
-
pid = "itemid-56"
|
|
136
|
-
item = Item.get_record_by_pid(pid)
|
|
137
|
-
ItemIndexer().index(item)
|
|
138
|
-
|
|
139
|
-
referenced = _assert_origin(indexer, ITEM_PID_TYPE, pid)
|
|
140
|
-
|
|
141
|
-
# should re-index loans
|
|
142
|
-
n_loans = 2 # from test data, including pending
|
|
143
|
-
_assert_contains(referenced, CIRCULATION_LOAN_PID_TYPE)
|
|
144
|
-
|
|
145
|
-
# should re-index document
|
|
146
|
-
n_documents = 1 # from test data
|
|
147
|
-
_assert_contains(referenced, DOCUMENT_PID_TYPE)
|
|
148
|
-
|
|
149
|
-
assert len(referenced) == n_loans + n_documents
|
|
150
|
-
|
|
151
|
-
def test_on_location_update():
|
|
152
|
-
"""Test location resolvers."""
|
|
153
|
-
indexer = _get_mock()
|
|
154
|
-
|
|
155
|
-
pid = "locid-2"
|
|
156
|
-
loc = Location.get_record_by_pid(pid)
|
|
157
|
-
LocationIndexer().index(loc)
|
|
158
|
-
|
|
159
|
-
referenced = _assert_origin(indexer, LOCATION_PID_TYPE, pid)
|
|
160
|
-
|
|
161
|
-
# should re-index internal locations
|
|
162
|
-
n_intlocs = 1 # from test data
|
|
163
|
-
_assert_contains(referenced, INTERNAL_LOCATION_PID_TYPE)
|
|
164
|
-
|
|
165
|
-
# should re-index internal items
|
|
166
|
-
n_items = 1 # from test data
|
|
167
|
-
_assert_contains(referenced, ITEM_PID_TYPE)
|
|
168
|
-
|
|
169
|
-
assert len(referenced) == n_intlocs + n_items
|
|
170
|
-
|
|
171
|
-
def test_on_patron_update():
|
|
172
|
-
"""Test patron resolvers."""
|
|
173
|
-
indexer = _get_mock()
|
|
174
|
-
|
|
175
|
-
pid = "2"
|
|
176
|
-
patron = Patron.get_patron(pid)
|
|
177
|
-
PatronIndexer().index(patron)
|
|
178
|
-
|
|
179
|
-
referenced = _assert_origin(indexer, PATRON_PID_TYPE, pid)
|
|
180
|
-
|
|
181
|
-
# should re-index loans
|
|
182
|
-
n_loans = 3 # from test data
|
|
183
|
-
_assert_contains(referenced, CIRCULATION_LOAN_PID_TYPE)
|
|
184
|
-
|
|
185
|
-
# should re-index document request
|
|
186
|
-
n_doc_req = 1 # from test data
|
|
187
|
-
_assert_contains(referenced, DOCUMENT_REQUEST_PID_TYPE)
|
|
188
|
-
|
|
189
|
-
# should re-index acq
|
|
190
|
-
n_acq = 1 # from test data
|
|
191
|
-
_assert_contains(referenced, ORDER_PID_TYPE)
|
|
192
|
-
|
|
193
|
-
# should re-index ill
|
|
194
|
-
n_ill = 3 # from test data
|
|
195
|
-
_assert_contains(referenced, BORROWING_REQUEST_PID_TYPE)
|
|
196
|
-
|
|
197
|
-
expected_total = n_loans + n_doc_req + n_acq + n_ill
|
|
198
|
-
assert len(referenced) == expected_total
|
|
199
|
-
|
|
200
|
-
test_on_document_update()
|
|
201
|
-
test_on_eitem_update()
|
|
202
|
-
test_on_internal_location_update()
|
|
203
|
-
test_on_item_update()
|
|
204
|
-
test_on_location_update()
|
|
205
|
-
test_on_patron_update()
|
tests/api/ils/test_stats.py
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2019 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test records relations."""
|
|
9
|
-
|
|
10
|
-
import json
|
|
11
|
-
|
|
12
|
-
from flask import url_for
|
|
13
|
-
from invenio_db import db
|
|
14
|
-
|
|
15
|
-
from invenio_app_ils.signals import record_viewed
|
|
16
|
-
from tests.helpers import user_login, user_logout
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _most_loaned_request(client, json_headers, from_date=None, to_date=None):
|
|
20
|
-
"""Perform a stats request."""
|
|
21
|
-
params = []
|
|
22
|
-
if from_date is not None:
|
|
23
|
-
params.append("from_date={}".format(from_date))
|
|
24
|
-
if to_date is not None:
|
|
25
|
-
params.append("to_date={}".format(to_date))
|
|
26
|
-
response = client.get(
|
|
27
|
-
"{}?{}".format(
|
|
28
|
-
url_for("invenio_app_ils_circulation_stats.most-loaned"),
|
|
29
|
-
"&".join(params),
|
|
30
|
-
),
|
|
31
|
-
headers=json_headers,
|
|
32
|
-
)
|
|
33
|
-
return json.loads(response.data.decode("utf-8"))
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _assert_most_loaned(client, json_headers, from_date, to_date, expect):
|
|
37
|
-
"""Assert most loaned request."""
|
|
38
|
-
resp = _most_loaned_request(client, json_headers, from_date, to_date)
|
|
39
|
-
hits = resp["hits"]["hits"]
|
|
40
|
-
assert len(hits) == len(expect)
|
|
41
|
-
for hit in hits:
|
|
42
|
-
pid = hit["metadata"]["pid"]
|
|
43
|
-
assert hit["metadata"]["loan_count"] == expect[pid]["loans"]
|
|
44
|
-
assert hit["metadata"]["loan_extensions"] == expect[pid]["extensions"]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def test_stats_most_loaned_documents(client, json_headers, testdata_most_loaned, users):
|
|
48
|
-
"""Test most loaned documents API endpoint."""
|
|
49
|
-
user_login(client, "librarian", users)
|
|
50
|
-
|
|
51
|
-
# Dates covering all loans
|
|
52
|
-
_assert_most_loaned(
|
|
53
|
-
client,
|
|
54
|
-
json_headers,
|
|
55
|
-
"2019-01-01",
|
|
56
|
-
"2019-12-01",
|
|
57
|
-
expect={
|
|
58
|
-
"docid-1": dict(loans=3, extensions=0),
|
|
59
|
-
"docid-2": dict(loans=1, extensions=1),
|
|
60
|
-
"docid-3": dict(loans=2, extensions=6),
|
|
61
|
-
"docid-5": dict(loans=1, extensions=0),
|
|
62
|
-
},
|
|
63
|
-
)
|
|
64
|
-
# Test checking range which should be empty
|
|
65
|
-
_assert_most_loaned(client, json_headers, "2019-01-01", "2019-01-01", expect={})
|
|
66
|
-
# Test range only including the first loan
|
|
67
|
-
_assert_most_loaned(
|
|
68
|
-
client,
|
|
69
|
-
json_headers,
|
|
70
|
-
"2019-01-01",
|
|
71
|
-
"2019-01-03",
|
|
72
|
-
expect={"docid-1": dict(loans=1, extensions=0)},
|
|
73
|
-
)
|
|
74
|
-
_assert_most_loaned(
|
|
75
|
-
client,
|
|
76
|
-
json_headers,
|
|
77
|
-
"2019-02-02",
|
|
78
|
-
"2019-03-02",
|
|
79
|
-
expect={
|
|
80
|
-
"docid-1": dict(loans=2, extensions=0),
|
|
81
|
-
"docid-2": dict(loans=1, extensions=1),
|
|
82
|
-
"docid-3": dict(loans=1, extensions=3),
|
|
83
|
-
},
|
|
84
|
-
)
|
|
85
|
-
_assert_most_loaned(
|
|
86
|
-
client,
|
|
87
|
-
json_headers,
|
|
88
|
-
"2019-05-20",
|
|
89
|
-
"2019-08-22",
|
|
90
|
-
expect={"docid-3": dict(loans=1, extensions=3)},
|
|
91
|
-
)
|
|
92
|
-
# outside end range
|
|
93
|
-
_assert_most_loaned(client, json_headers, "2019-05-21", "2019-12-31", expect={})
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def test_stats_downloads_views_permissions(
|
|
97
|
-
client, json_headers, users, testdata, mocker
|
|
98
|
-
):
|
|
99
|
-
"""Test permissions on downloads/views stats."""
|
|
100
|
-
|
|
101
|
-
# mock record_viewed signal because travis timed out and the signal does
|
|
102
|
-
# not need to be tested here
|
|
103
|
-
mocker.patch(
|
|
104
|
-
"invenio_app_ils.signals.record_viewed.send",
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
url_open_access = url_for(
|
|
108
|
-
"ils_document_stats.docid_stats", pid_value="docid-open-access"
|
|
109
|
-
)
|
|
110
|
-
url_closed_access = url_for(
|
|
111
|
-
"ils_document_stats.docid_stats", pid_value="docid-closed-access"
|
|
112
|
-
)
|
|
113
|
-
params = {}
|
|
114
|
-
params["event"] = "record-view"
|
|
115
|
-
|
|
116
|
-
# permission for librarian
|
|
117
|
-
user_login(client, "librarian", users)
|
|
118
|
-
res = client.post(url_open_access, headers=json_headers, data=json.dumps(params))
|
|
119
|
-
assert res.status_code == 202
|
|
120
|
-
|
|
121
|
-
res = client.post(url_closed_access, headers=json_headers, data=json.dumps(params))
|
|
122
|
-
assert res.status_code == 202
|
|
123
|
-
|
|
124
|
-
user_logout(client)
|
|
125
|
-
|
|
126
|
-
# permission for patron
|
|
127
|
-
user_login(client, "patron1", users)
|
|
128
|
-
|
|
129
|
-
res = client.post(url_open_access, headers=json_headers, data=json.dumps(params))
|
|
130
|
-
assert res.status_code == 202
|
|
131
|
-
|
|
132
|
-
res = client.post(url_closed_access, headers=json_headers, data=json.dumps(params))
|
|
133
|
-
assert res.status_code == 403
|
|
134
|
-
|
|
135
|
-
user_logout(client)
|
|
136
|
-
|
|
137
|
-
# permission for unauthenticated user
|
|
138
|
-
res = client.post(url_open_access, headers=json_headers, data=json.dumps(params))
|
|
139
|
-
assert res.status_code == 202
|
|
140
|
-
|
|
141
|
-
res = client.post(url_closed_access, headers=json_headers, data=json.dumps(params))
|
|
142
|
-
assert res.status_code == 401
|
tests/api/ils/test_tasks.py
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2020 CERN.
|
|
4
|
-
#
|
|
5
|
-
# invenio-app-ils is free software; you can redistribute it and/or modify it
|
|
6
|
-
# under the terms of the MIT License; see LICENSE file for more details.
|
|
7
|
-
|
|
8
|
-
"""Test location closure cronjobs."""
|
|
9
|
-
|
|
10
|
-
import time
|
|
11
|
-
|
|
12
|
-
import arrow
|
|
13
|
-
from invenio_circulation.proxies import current_circulation
|
|
14
|
-
|
|
15
|
-
from invenio_app_ils.circulation.search import get_active_loans
|
|
16
|
-
from invenio_app_ils.closures.tasks import (
|
|
17
|
-
clean_locations_past_closures_exceptions,
|
|
18
|
-
extend_active_loans_location_closure,
|
|
19
|
-
)
|
|
20
|
-
from invenio_app_ils.proxies import current_app_ils
|
|
21
|
-
|
|
22
|
-
_LOCATION_PID_1 = "locid-1"
|
|
23
|
-
_LOCATION_PID_2 = "locid-2"
|
|
24
|
-
_LOAN_PID_1 = "loanid-6"
|
|
25
|
-
_LOAN_PID_2 = "loanid-5"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
_OPENING_EXCEPTIONS_WITH_PAST_EXCEPTIONS = [
|
|
29
|
-
{
|
|
30
|
-
"title": "Past holidays",
|
|
31
|
-
"is_open": False,
|
|
32
|
-
"start_date": "2010-01-01",
|
|
33
|
-
"end_date": "2010-01-06",
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"title": "Past holidays",
|
|
37
|
-
"is_open": False,
|
|
38
|
-
"start_date": "2013-04-05",
|
|
39
|
-
"end_date": "2013-04-08",
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
"title": "Past holiday",
|
|
43
|
-
"is_open": False,
|
|
44
|
-
"start_date": "2005-05-14",
|
|
45
|
-
"end_date": "2005-05-16",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"title": "Past holidays",
|
|
49
|
-
"is_open": False,
|
|
50
|
-
"start_date": "2019-02-01",
|
|
51
|
-
"end_date": "2019-02-06",
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"title": "Future holidays",
|
|
55
|
-
"is_open": False,
|
|
56
|
-
"start_date": "2100-02-11",
|
|
57
|
-
"end_date": "2100-02-12",
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"title": "Future holidays",
|
|
61
|
-
"is_open": False,
|
|
62
|
-
"start_date": "2100-03-01",
|
|
63
|
-
"end_date": "2100-03-06",
|
|
64
|
-
},
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
_OPENING_EXCEPTIONS_WITHOUT_CHANGES = [
|
|
68
|
-
{
|
|
69
|
-
"title": "Future holidays",
|
|
70
|
-
"is_open": False,
|
|
71
|
-
"start_date": "2110-01-01",
|
|
72
|
-
"end_date": "2110-01-06",
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"title": "Future holidays",
|
|
76
|
-
"is_open": False,
|
|
77
|
-
"start_date": "2113-04-05",
|
|
78
|
-
"end_date": "2113-04-08",
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"title": "Future holidays",
|
|
82
|
-
"is_open": False,
|
|
83
|
-
"start_date": "2105-05-14",
|
|
84
|
-
"end_date": "2105-05-16",
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"title": "Future holidays",
|
|
88
|
-
"is_open": False,
|
|
89
|
-
"start_date": "2119-02-01",
|
|
90
|
-
"end_date": "2119-02-06",
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"title": "Future holidays",
|
|
94
|
-
"is_open": False,
|
|
95
|
-
"start_date": "2100-02-11",
|
|
96
|
-
"end_date": "2100-02-12",
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"title": "Future holidays",
|
|
100
|
-
"is_open": False,
|
|
101
|
-
"start_date": "2100-03-01",
|
|
102
|
-
"end_date": "2100-03-06",
|
|
103
|
-
},
|
|
104
|
-
]
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def prepare_data(db, location_pid, opening_exceptions):
|
|
108
|
-
"""Adds some opening exceptions to a concrete location."""
|
|
109
|
-
|
|
110
|
-
location = current_app_ils.location_record_cls
|
|
111
|
-
record = location.get_record_by_pid(location_pid)
|
|
112
|
-
|
|
113
|
-
record.update({"opening_exceptions": opening_exceptions})
|
|
114
|
-
record.commit()
|
|
115
|
-
db.session.commit()
|
|
116
|
-
current_app_ils.location_indexer.index(record)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def prepare_loans_data(db, testdata):
|
|
120
|
-
"""Updates the expire date of a loan to match a closure day."""
|
|
121
|
-
loans = testdata["loans"]
|
|
122
|
-
|
|
123
|
-
def new_expiration_date(loan_test_data, date):
|
|
124
|
-
loan = current_circulation.loan_record_cls.get_record_by_pid(
|
|
125
|
-
loan_test_data["pid"]
|
|
126
|
-
)
|
|
127
|
-
loan.update(end_date=date.isoformat())
|
|
128
|
-
loan.commit()
|
|
129
|
-
db.session.commit()
|
|
130
|
-
current_circulation.loan_indexer().index(loan)
|
|
131
|
-
|
|
132
|
-
# Change expiration date to match closure
|
|
133
|
-
date = arrow.get("2100-03-03").date()
|
|
134
|
-
date_2 = arrow.get("2100-02-12").date()
|
|
135
|
-
new_expiration_date(loans[5], date)
|
|
136
|
-
new_expiration_date(loans[4], date_2)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def opening_exceptions_cleaned():
|
|
140
|
-
"""Prepare data that should match with the response."""
|
|
141
|
-
opening_exceptions = [
|
|
142
|
-
{
|
|
143
|
-
"title": "Future holidays",
|
|
144
|
-
"is_open": False,
|
|
145
|
-
"start_date": "2100-02-11",
|
|
146
|
-
"end_date": "2100-02-12",
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
"title": "Future holidays",
|
|
150
|
-
"is_open": False,
|
|
151
|
-
"start_date": "2100-03-01",
|
|
152
|
-
"end_date": "2100-03-06",
|
|
153
|
-
},
|
|
154
|
-
]
|
|
155
|
-
|
|
156
|
-
return opening_exceptions
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def test_clean_locations_past_closures_exceptions(db, users, testdata):
|
|
160
|
-
"""Test cleaning of past exceptions."""
|
|
161
|
-
prepare_data(db, _LOCATION_PID_1, _OPENING_EXCEPTIONS_WITH_PAST_EXCEPTIONS)
|
|
162
|
-
prepare_data(db, _LOCATION_PID_2, _OPENING_EXCEPTIONS_WITH_PAST_EXCEPTIONS)
|
|
163
|
-
clean_locations_past_closures_exceptions()
|
|
164
|
-
location = current_app_ils.location_record_cls
|
|
165
|
-
record = location.get_record_by_pid(_LOCATION_PID_1)
|
|
166
|
-
record_2 = location.get_record_by_pid(_LOCATION_PID_2)
|
|
167
|
-
|
|
168
|
-
assert record["opening_exceptions"] == opening_exceptions_cleaned()
|
|
169
|
-
assert record_2["opening_exceptions"] == opening_exceptions_cleaned()
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def test_no_changes_on_loans(db, users, testdata):
|
|
173
|
-
"""Test that no changes are applied to the loans if not needed."""
|
|
174
|
-
prepare_data(db, _LOCATION_PID_1, _OPENING_EXCEPTIONS_WITHOUT_CHANGES)
|
|
175
|
-
loans = testdata["loans"]
|
|
176
|
-
clean_locations_past_closures_exceptions()
|
|
177
|
-
for hit in get_active_loans().scan():
|
|
178
|
-
for loan in loans:
|
|
179
|
-
if hit["pid"] == loan["pid"]:
|
|
180
|
-
assert hit["end_date"] == loan["end_date"]
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def test_no_changes_on_exceptions(db, users, testdata):
|
|
184
|
-
"""Test that no changes are applied to the exceptions if not needed."""
|
|
185
|
-
prepare_data(db, _LOCATION_PID_1, _OPENING_EXCEPTIONS_WITHOUT_CHANGES)
|
|
186
|
-
extend_active_loans_location_closure(_LOCATION_PID_1)
|
|
187
|
-
location = current_app_ils.location_record_cls
|
|
188
|
-
record = location.get_record_by_pid(_LOCATION_PID_1)
|
|
189
|
-
record_2 = location.get_record_by_pid(_LOCATION_PID_2)
|
|
190
|
-
|
|
191
|
-
assert record["opening_exceptions"] == _OPENING_EXCEPTIONS_WITHOUT_CHANGES
|
|
192
|
-
assert record_2["opening_exceptions"] == []
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def test_extend_active_loans_location_closure(db, users, testdata, app_with_notifs):
|
|
196
|
-
"""Test extension of active loans that finish on holidays."""
|
|
197
|
-
prepare_data(db, _LOCATION_PID_1, _OPENING_EXCEPTIONS_WITH_PAST_EXCEPTIONS)
|
|
198
|
-
prepare_loans_data(db, testdata)
|
|
199
|
-
time.sleep(3)
|
|
200
|
-
|
|
201
|
-
with app_with_notifs.extensions["mail"].record_messages() as outbox:
|
|
202
|
-
assert len(outbox) == 0
|
|
203
|
-
extend_active_loans_location_closure(_LOCATION_PID_1)
|
|
204
|
-
assert len(outbox) == 2
|
|
205
|
-
|
|
206
|
-
loan = current_circulation.loan_record_cls.get_record_by_pid(_LOAN_PID_1)
|
|
207
|
-
loan_2 = current_circulation.loan_record_cls.get_record_by_pid(_LOAN_PID_2)
|
|
208
|
-
assert loan["end_date"] == "2100-03-07"
|
|
209
|
-
assert loan_2["end_date"] == "2100-02-13"
|