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,150 +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 loan update."""
|
|
9
|
-
import json
|
|
10
|
-
from copy import deepcopy
|
|
11
|
-
from datetime import date, timedelta
|
|
12
|
-
|
|
13
|
-
from flask import url_for
|
|
14
|
-
from invenio_circulation.api import Loan
|
|
15
|
-
|
|
16
|
-
from tests.helpers import user_login, user_logout
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _url_loan(pid_value):
|
|
20
|
-
return url_for("invenio_records_rest.loanid_item", pid_value=pid_value)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _post_loan_update(
|
|
24
|
-
client,
|
|
25
|
-
json_headers,
|
|
26
|
-
pid_value,
|
|
27
|
-
start_date=None,
|
|
28
|
-
end_date=None,
|
|
29
|
-
request_start_date=None,
|
|
30
|
-
request_expire_date=None,
|
|
31
|
-
):
|
|
32
|
-
url = url_for(
|
|
33
|
-
"invenio_app_ils_circulation.loanid_update_dates", pid_value=pid_value
|
|
34
|
-
)
|
|
35
|
-
data = {}
|
|
36
|
-
if start_date:
|
|
37
|
-
data["start_date"] = start_date
|
|
38
|
-
if end_date:
|
|
39
|
-
data["end_date"] = end_date
|
|
40
|
-
if request_start_date:
|
|
41
|
-
data["request_start_date"] = request_start_date
|
|
42
|
-
if request_expire_date:
|
|
43
|
-
data["request_expire_date"] = request_expire_date
|
|
44
|
-
res = client.post(url, headers=json_headers, data=json.dumps(data))
|
|
45
|
-
return res
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _load_result(res):
|
|
49
|
-
return json.loads(res.data.decode("utf-8"))
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def _today(dt=0):
|
|
53
|
-
return (date.today() + timedelta(days=dt)).strftime("%Y-%m-%d")
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def test_loan_access_permission(client, json_headers, users, testdata):
|
|
57
|
-
"""
|
|
58
|
-
Test that a patron should not be able to update their loan;
|
|
59
|
-
and a fortiori, other people's.
|
|
60
|
-
"""
|
|
61
|
-
loan = testdata["loans"][0]
|
|
62
|
-
user = user_login(client, "patron1", users)
|
|
63
|
-
url = _url_loan(loan["pid"])
|
|
64
|
-
res = client.get(url, headers=json_headers)
|
|
65
|
-
assert res.status_code == 200 # Can access their own loan
|
|
66
|
-
metadata = _load_result(res)["metadata"]
|
|
67
|
-
metadata["transaction_user_pid"] = str(user.id)
|
|
68
|
-
res = client.put(url, headers=json_headers, data=json.dumps(metadata))
|
|
69
|
-
assert res.status_code == 403 # Cannot modify the loan
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_loan_update_date(client, json_headers, users, testdata):
|
|
73
|
-
"""Test the edition of the dates on a loan."""
|
|
74
|
-
pid = testdata["loans"][4]["pid"] # Item on loan
|
|
75
|
-
|
|
76
|
-
res = _post_loan_update(client, json_headers, pid)
|
|
77
|
-
assert res.status_code == 401
|
|
78
|
-
|
|
79
|
-
# Patron cannot update loan
|
|
80
|
-
user_login(client, "patron1", users)
|
|
81
|
-
res = _post_loan_update(client, json_headers, pid)
|
|
82
|
-
assert res.status_code == 403
|
|
83
|
-
user_logout(client)
|
|
84
|
-
|
|
85
|
-
# Librarian can
|
|
86
|
-
user_login(client, "librarian", users)
|
|
87
|
-
old_loan = _load_result(client.get(_url_loan(pid), headers=json_headers))
|
|
88
|
-
res = _post_loan_update(client, json_headers, pid)
|
|
89
|
-
assert res.status_code == 202
|
|
90
|
-
new_loan = _load_result(res)
|
|
91
|
-
assert old_loan["updated"] < new_loan["updated"] # Liveness check
|
|
92
|
-
|
|
93
|
-
# Update both values
|
|
94
|
-
start_date = "2020-01-01"
|
|
95
|
-
end_date = _today(+1)
|
|
96
|
-
res = _post_loan_update(
|
|
97
|
-
client, json_headers, pid, start_date=start_date, end_date=end_date
|
|
98
|
-
)
|
|
99
|
-
assert res.status_code == 202
|
|
100
|
-
new_loan_meta = _load_result(res)["metadata"]
|
|
101
|
-
assert new_loan_meta["start_date"] == start_date
|
|
102
|
-
assert new_loan_meta["end_date"] == end_date
|
|
103
|
-
|
|
104
|
-
# Start date after today
|
|
105
|
-
start_date = _today(+2)
|
|
106
|
-
end_date = _today(+3)
|
|
107
|
-
res = _post_loan_update(
|
|
108
|
-
client, json_headers, pid, start_date=start_date, end_date=end_date
|
|
109
|
-
)
|
|
110
|
-
assert res.status_code == 400
|
|
111
|
-
|
|
112
|
-
# No relative constraints on non-active loans
|
|
113
|
-
pid = testdata["loans"][3]["pid"] # Pending
|
|
114
|
-
res = _post_loan_update(
|
|
115
|
-
client,
|
|
116
|
-
json_headers,
|
|
117
|
-
pid,
|
|
118
|
-
request_start_date=start_date,
|
|
119
|
-
request_expire_date=end_date,
|
|
120
|
-
)
|
|
121
|
-
assert res.status_code == 202
|
|
122
|
-
new_loan_meta = _load_result(res)["metadata"]
|
|
123
|
-
assert new_loan_meta["request_start_date"] == start_date
|
|
124
|
-
assert new_loan_meta["request_expire_date"] == end_date
|
|
125
|
-
|
|
126
|
-
# Negative date range
|
|
127
|
-
start_date = "2000-02-01"
|
|
128
|
-
end_date = "2000-01-01"
|
|
129
|
-
res = _post_loan_update(
|
|
130
|
-
client,
|
|
131
|
-
json_headers,
|
|
132
|
-
pid,
|
|
133
|
-
request_start_date=start_date,
|
|
134
|
-
request_expire_date=end_date,
|
|
135
|
-
)
|
|
136
|
-
assert res.status_code == 400
|
|
137
|
-
|
|
138
|
-
# Illegal combination of parameters
|
|
139
|
-
start_date = _today(-2)
|
|
140
|
-
end_date = _today(+2)
|
|
141
|
-
res = _post_loan_update(
|
|
142
|
-
client,
|
|
143
|
-
json_headers,
|
|
144
|
-
pid,
|
|
145
|
-
start_date=start_date,
|
|
146
|
-
end_date=end_date,
|
|
147
|
-
request_start_date=start_date,
|
|
148
|
-
request_expire_date=end_date,
|
|
149
|
-
)
|
|
150
|
-
assert res.status_code == 400
|
|
@@ -1,221 +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 email notifications on transitions."""
|
|
9
|
-
|
|
10
|
-
import random
|
|
11
|
-
from datetime import timedelta
|
|
12
|
-
|
|
13
|
-
import arrow
|
|
14
|
-
from flask import current_app, url_for
|
|
15
|
-
from invenio_circulation.api import Loan
|
|
16
|
-
from invenio_circulation.proxies import current_circulation
|
|
17
|
-
from invenio_indexer.api import RecordIndexer
|
|
18
|
-
from invenio_search import current_search
|
|
19
|
-
|
|
20
|
-
from invenio_app_ils.circulation.notifications.tasks import (
|
|
21
|
-
send_expiring_loans_notification_reminder,
|
|
22
|
-
send_overdue_loans_notification_reminder,
|
|
23
|
-
)
|
|
24
|
-
from invenio_app_ils.documents.api import Document
|
|
25
|
-
from invenio_app_ils.patrons.api import Patron
|
|
26
|
-
from tests.helpers import user_login, user_logout
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def test_notifs_on_loan_checkout(client, app_with_notifs, users, testdata, loan_params):
|
|
30
|
-
"""Test that an email is sent on loan checkout."""
|
|
31
|
-
loan_data = testdata["loans"][1]
|
|
32
|
-
loan = Loan.get_record_by_pid(loan_data["pid"])
|
|
33
|
-
with app_with_notifs.extensions["mail"].record_messages() as outbox:
|
|
34
|
-
user_login(client, "admin", users)
|
|
35
|
-
|
|
36
|
-
assert len(outbox) == 0
|
|
37
|
-
current_circulation.circulation.trigger(
|
|
38
|
-
loan, **dict(loan_params, trigger="checkout")
|
|
39
|
-
)
|
|
40
|
-
assert len(outbox) == 1
|
|
41
|
-
msg = outbox[0]
|
|
42
|
-
|
|
43
|
-
doc = Document.get_record_by_pid(loan_data["document_pid"])
|
|
44
|
-
expected_subject = 'InvenioILS: loan started for "{0}"'.format(doc["title"])
|
|
45
|
-
assert msg.subject == expected_subject
|
|
46
|
-
|
|
47
|
-
edition_year = " ({edition} - {year})".format(
|
|
48
|
-
edition=doc["edition"], year=doc["publication_year"]
|
|
49
|
-
)
|
|
50
|
-
full_title = "{title}, {author}{edition_year}".format(
|
|
51
|
-
title=doc["title"],
|
|
52
|
-
author=doc["authors"][0]["full_name"],
|
|
53
|
-
edition_year=edition_year,
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
literature_url = "{host}{path}".format(
|
|
57
|
-
host=current_app.config["SPA_HOST"],
|
|
58
|
-
path=current_app.config["SPA_PATHS"]["literature"] % {"pid": doc["pid"]},
|
|
59
|
-
)
|
|
60
|
-
profile_url = "{host}{path}".format(
|
|
61
|
-
host=current_app.config["SPA_HOST"],
|
|
62
|
-
path=current_app.config["SPA_PATHS"]["profile"],
|
|
63
|
-
)
|
|
64
|
-
expected_body_plain = """Dear Patron One,
|
|
65
|
-
|
|
66
|
-
your loan for "{doc_full_title}" <{literature_url}> has started.
|
|
67
|
-
|
|
68
|
-
The due date is {loan_end_date}.
|
|
69
|
-
|
|
70
|
-
You can see your ongoing and past loans in your profile page <{profile_url}>.
|
|
71
|
-
|
|
72
|
-
Kind regards,
|
|
73
|
-
InvenioILS""".format(
|
|
74
|
-
doc_full_title=full_title,
|
|
75
|
-
literature_url=literature_url,
|
|
76
|
-
loan_end_date=loan_data["end_date"],
|
|
77
|
-
profile_url=profile_url,
|
|
78
|
-
)
|
|
79
|
-
assert msg.body == expected_body_plain
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def test_notification_on_overdue_permissions(client, testdata, json_headers, users):
|
|
83
|
-
"""Test that only the backoffice can send a reminder."""
|
|
84
|
-
pid = testdata["loans"][0]["pid"]
|
|
85
|
-
url = url_for("invenio_app_ils_circulation.loanid_notification", pid_value=pid)
|
|
86
|
-
tests = [("patron1", 403), ("anonymous", 401)]
|
|
87
|
-
for username, expected_status in tests:
|
|
88
|
-
user_login(client, username, users)
|
|
89
|
-
res = client.post(url, headers=json_headers)
|
|
90
|
-
assert res.status_code == expected_status
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def test_notification_on_overdue_loans(
|
|
94
|
-
app_with_notifs, db, users, testdata, mocker, client, json_headers
|
|
95
|
-
):
|
|
96
|
-
"""Test that an email is sent for a loan that is overdue."""
|
|
97
|
-
mocker.patch(
|
|
98
|
-
"invenio_app_ils.patrons.api.Patron.get_patron",
|
|
99
|
-
return_value=Patron(users["patron1"].id),
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
def prepare_data():
|
|
103
|
-
"""Prepare data."""
|
|
104
|
-
days = current_app.config[
|
|
105
|
-
"ILS_CIRCULATION_NOTIFICATION_OVERDUE_REMINDER_INTERVAL"
|
|
106
|
-
]
|
|
107
|
-
loans = testdata["loans"]
|
|
108
|
-
|
|
109
|
-
recs = []
|
|
110
|
-
now = arrow.utcnow()
|
|
111
|
-
|
|
112
|
-
def new_end_date(loan, date):
|
|
113
|
-
loan["end_date"] = date.date().isoformat()
|
|
114
|
-
loan["state"] = "ITEM_ON_LOAN"
|
|
115
|
-
loan.commit()
|
|
116
|
-
recs.append(loan)
|
|
117
|
-
|
|
118
|
-
# overdue loans
|
|
119
|
-
date = now - timedelta(days=days)
|
|
120
|
-
new_end_date(loans[0], date)
|
|
121
|
-
|
|
122
|
-
date = now - timedelta(days=days * 2)
|
|
123
|
-
new_end_date(loans[1], date)
|
|
124
|
-
|
|
125
|
-
# not overdue
|
|
126
|
-
date = now - timedelta(days=-1)
|
|
127
|
-
new_end_date(loans[2], date)
|
|
128
|
-
|
|
129
|
-
# not overdue or overdue but not to be notified
|
|
130
|
-
remaining_not_overdue = loans[3:]
|
|
131
|
-
for loan in remaining_not_overdue:
|
|
132
|
-
days = random.choice([-1, 0, 1])
|
|
133
|
-
date = now - timedelta(days=days)
|
|
134
|
-
new_end_date(loan, date)
|
|
135
|
-
db.session.commit()
|
|
136
|
-
|
|
137
|
-
indexer = RecordIndexer()
|
|
138
|
-
for rec in recs:
|
|
139
|
-
indexer.index(rec)
|
|
140
|
-
|
|
141
|
-
current_search.flush_and_refresh(index="*")
|
|
142
|
-
|
|
143
|
-
user_login(client, "librarian", users)
|
|
144
|
-
|
|
145
|
-
# test individual overdue loan
|
|
146
|
-
prepare_data()
|
|
147
|
-
loans = testdata["loans"]
|
|
148
|
-
|
|
149
|
-
notification_url = url_for(
|
|
150
|
-
"invenio_app_ils_circulation.loanid_notification",
|
|
151
|
-
pid_value=loans[0]["pid"],
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
res = client.post(notification_url, headers=json_headers)
|
|
155
|
-
assert res.status_code == 202
|
|
156
|
-
|
|
157
|
-
# test individual not overdue loan
|
|
158
|
-
notification_url = url_for(
|
|
159
|
-
"invenio_app_ils_circulation.loanid_notification",
|
|
160
|
-
pid_value=loans[2]["pid"],
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
res = client.post(notification_url, headers=json_headers)
|
|
164
|
-
assert res.status_code == 400
|
|
165
|
-
|
|
166
|
-
user_logout(client)
|
|
167
|
-
|
|
168
|
-
# test all loans
|
|
169
|
-
with app_with_notifs.extensions["mail"].record_messages() as outbox:
|
|
170
|
-
assert len(outbox) == 0
|
|
171
|
-
send_overdue_loans_notification_reminder.apply_async()
|
|
172
|
-
assert len(outbox) == 2
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def test_notification_on_expiring_loans(app_with_notifs, db, users, testdata, mocker):
|
|
176
|
-
"""Test that an email is sent for a loan that is about to expire."""
|
|
177
|
-
mocker.patch(
|
|
178
|
-
"invenio_app_ils.patrons.api.Patron.get_patron",
|
|
179
|
-
return_value=Patron(users["patron1"].id),
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
def prepare_data():
|
|
183
|
-
"""Prepare data."""
|
|
184
|
-
max_days = current_app.config["ILS_CIRCULATION_LOAN_WILL_EXPIRE_DAYS"]
|
|
185
|
-
loans = testdata["loans"]
|
|
186
|
-
|
|
187
|
-
recs = []
|
|
188
|
-
now = arrow.utcnow()
|
|
189
|
-
|
|
190
|
-
def new_end_date(loan, date):
|
|
191
|
-
loan["end_date"] = date.date().isoformat()
|
|
192
|
-
loan["state"] = "ITEM_ON_LOAN"
|
|
193
|
-
loan.commit()
|
|
194
|
-
recs.append(loan)
|
|
195
|
-
|
|
196
|
-
# expiring loans
|
|
197
|
-
date = now + timedelta(days=max_days)
|
|
198
|
-
new_end_date(loans[0], date)
|
|
199
|
-
new_end_date(loans[1], date)
|
|
200
|
-
new_end_date(loans[2], date)
|
|
201
|
-
|
|
202
|
-
# not expiring
|
|
203
|
-
remaining_not_overdue = loans[3:]
|
|
204
|
-
for loan in remaining_not_overdue:
|
|
205
|
-
days = random.choice([-2, -1, 1, max_days + 1])
|
|
206
|
-
date = now + timedelta(days=days)
|
|
207
|
-
new_end_date(loan, date)
|
|
208
|
-
db.session.commit()
|
|
209
|
-
|
|
210
|
-
indexer = RecordIndexer()
|
|
211
|
-
for rec in recs:
|
|
212
|
-
indexer.index(rec)
|
|
213
|
-
|
|
214
|
-
current_search.flush_and_refresh(index="*")
|
|
215
|
-
|
|
216
|
-
prepare_data()
|
|
217
|
-
|
|
218
|
-
with app_with_notifs.extensions["mail"].record_messages() as outbox:
|
|
219
|
-
assert len(outbox) == 0
|
|
220
|
-
send_expiring_loans_notification_reminder.apply_async()
|
|
221
|
-
assert len(outbox) == 3
|
tests/api/conftest.py
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2018-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
|
-
"""Pytest fixtures and plugins for the API application."""
|
|
9
|
-
|
|
10
|
-
import tempfile
|
|
11
|
-
|
|
12
|
-
import pytest
|
|
13
|
-
from invenio_app.factory import create_api
|
|
14
|
-
from invenio_circulation.api import Loan
|
|
15
|
-
from invenio_circulation.pidstore.pids import CIRCULATION_LOAN_PID_TYPE
|
|
16
|
-
from invenio_indexer.api import RecordIndexer
|
|
17
|
-
from invenio_search import current_search
|
|
18
|
-
|
|
19
|
-
from invenio_app_ils.acquisition.api import ORDER_PID_TYPE, Order
|
|
20
|
-
from invenio_app_ils.document_requests.api import (
|
|
21
|
-
DOCUMENT_REQUEST_PID_TYPE,
|
|
22
|
-
DocumentRequest,
|
|
23
|
-
)
|
|
24
|
-
from invenio_app_ils.documents.api import DOCUMENT_PID_TYPE, Document
|
|
25
|
-
from invenio_app_ils.eitems.api import EITEM_PID_TYPE, EItem
|
|
26
|
-
from invenio_app_ils.ill.api import BORROWING_REQUEST_PID_TYPE, BorrowingRequest
|
|
27
|
-
from invenio_app_ils.internal_locations.api import (
|
|
28
|
-
INTERNAL_LOCATION_PID_TYPE,
|
|
29
|
-
InternalLocation,
|
|
30
|
-
)
|
|
31
|
-
from invenio_app_ils.items.api import ITEM_PID_TYPE, Item
|
|
32
|
-
from invenio_app_ils.locations.api import LOCATION_PID_TYPE, Location
|
|
33
|
-
from invenio_app_ils.providers.api import PROVIDER_PID_TYPE, Provider
|
|
34
|
-
from invenio_app_ils.series.api import SERIES_PID_TYPE, Series
|
|
35
|
-
from tests.helpers import (
|
|
36
|
-
document_ref_builder,
|
|
37
|
-
internal_location_ref_builder,
|
|
38
|
-
load_json_from_datadir,
|
|
39
|
-
mint_record_pid,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@pytest.fixture(scope="module")
|
|
44
|
-
def app_config(app_config):
|
|
45
|
-
"""Get app config."""
|
|
46
|
-
tests_config = {
|
|
47
|
-
"REST_CSRF_ENABLED": False,
|
|
48
|
-
"ACCOUNTS_SESSION_REDIS_URL": "", # in-memory
|
|
49
|
-
"RATELIMIT_GUEST_USER": "1000 per minute",
|
|
50
|
-
"RATELIMIT_AUTHENTICATED_USER": "1000 per minute",
|
|
51
|
-
"CIRCULATION_TRANSACTION_USER_VALIDATOR": lambda x: True,
|
|
52
|
-
"EXTEND_LOANS_LOCATION_UPDATED": False,
|
|
53
|
-
"ILS_NOTIFICATIONS_TEMPLATES": {"footer": "footer.html"},
|
|
54
|
-
}
|
|
55
|
-
app_config.update(tests_config)
|
|
56
|
-
return app_config
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@pytest.fixture(scope="module")
|
|
60
|
-
def create_app():
|
|
61
|
-
"""Create test app."""
|
|
62
|
-
return create_api
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@pytest.fixture()
|
|
66
|
-
def json_headers():
|
|
67
|
-
"""JSON headers."""
|
|
68
|
-
return [
|
|
69
|
-
("Content-Type", "application/json"),
|
|
70
|
-
("Accept", "application/json"),
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _create_records(db, objs, cls, pid_type):
|
|
75
|
-
"""Create records and index."""
|
|
76
|
-
recs = []
|
|
77
|
-
for obj in objs:
|
|
78
|
-
record = cls.create(obj)
|
|
79
|
-
mint_record_pid(pid_type, "pid", record)
|
|
80
|
-
recs.append(record)
|
|
81
|
-
db.session.commit()
|
|
82
|
-
return recs
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@pytest.fixture()
|
|
86
|
-
def testdata(app, db, search_clear, users):
|
|
87
|
-
"""Create, index and return test data."""
|
|
88
|
-
data = load_json_from_datadir("locations.json")
|
|
89
|
-
locations = _create_records(db, data, Location, LOCATION_PID_TYPE)
|
|
90
|
-
|
|
91
|
-
data = load_json_from_datadir("internal_locations.json")
|
|
92
|
-
int_locs = _create_records(db, data, InternalLocation, INTERNAL_LOCATION_PID_TYPE)
|
|
93
|
-
|
|
94
|
-
data = load_json_from_datadir("series.json")
|
|
95
|
-
series = _create_records(db, data, Series, SERIES_PID_TYPE)
|
|
96
|
-
|
|
97
|
-
data = load_json_from_datadir("documents.json")
|
|
98
|
-
documents = _create_records(db, data, Document, DOCUMENT_PID_TYPE)
|
|
99
|
-
|
|
100
|
-
data = load_json_from_datadir("items.json")
|
|
101
|
-
items = _create_records(db, data, Item, ITEM_PID_TYPE)
|
|
102
|
-
|
|
103
|
-
data = load_json_from_datadir("eitems.json")
|
|
104
|
-
eitems = _create_records(db, data, EItem, EITEM_PID_TYPE)
|
|
105
|
-
|
|
106
|
-
data = load_json_from_datadir("loans.json")
|
|
107
|
-
loans = _create_records(db, data, Loan, CIRCULATION_LOAN_PID_TYPE)
|
|
108
|
-
|
|
109
|
-
data = load_json_from_datadir("acq_providers.json")
|
|
110
|
-
acq_providers = _create_records(db, data, Provider, PROVIDER_PID_TYPE)
|
|
111
|
-
|
|
112
|
-
data = load_json_from_datadir("acq_orders.json")
|
|
113
|
-
acq_orders = _create_records(db, data, Order, ORDER_PID_TYPE)
|
|
114
|
-
|
|
115
|
-
data = load_json_from_datadir("ill_providers.json")
|
|
116
|
-
ill_providers = _create_records(db, data, Provider, PROVIDER_PID_TYPE)
|
|
117
|
-
|
|
118
|
-
data = load_json_from_datadir("ill_borrowing_requests.json")
|
|
119
|
-
ill_brw_reqs = _create_records(
|
|
120
|
-
db, data, BorrowingRequest, BORROWING_REQUEST_PID_TYPE
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
data = load_json_from_datadir("document_requests.json")
|
|
124
|
-
doc_reqs = _create_records(db, data, DocumentRequest, DOCUMENT_REQUEST_PID_TYPE)
|
|
125
|
-
|
|
126
|
-
# index
|
|
127
|
-
ri = RecordIndexer()
|
|
128
|
-
for rec in (
|
|
129
|
-
locations
|
|
130
|
-
+ int_locs
|
|
131
|
-
+ series
|
|
132
|
-
+ documents
|
|
133
|
-
+ items
|
|
134
|
-
+ eitems
|
|
135
|
-
+ loans
|
|
136
|
-
+ doc_reqs
|
|
137
|
-
+ acq_providers
|
|
138
|
-
+ acq_orders
|
|
139
|
-
+ ill_providers
|
|
140
|
-
+ ill_brw_reqs
|
|
141
|
-
):
|
|
142
|
-
ri.index(rec)
|
|
143
|
-
|
|
144
|
-
current_search.flush_and_refresh(index="*")
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
"document_requests": doc_reqs,
|
|
148
|
-
"documents": documents,
|
|
149
|
-
"internal_locations": int_locs,
|
|
150
|
-
"items": items,
|
|
151
|
-
"eitems": eitems,
|
|
152
|
-
"loans": loans,
|
|
153
|
-
"locations": locations,
|
|
154
|
-
"series": series,
|
|
155
|
-
"acq_providers": acq_providers,
|
|
156
|
-
"acq_orders": acq_orders,
|
|
157
|
-
"ill_providers": ill_providers,
|
|
158
|
-
"ill_brw_reqs": ill_brw_reqs,
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@pytest.fixture()
|
|
163
|
-
def testdata_most_loaned(db, testdata):
|
|
164
|
-
"""Create, index and return test data for most loans tests."""
|
|
165
|
-
most_loaned = load_json_from_datadir("loans_most_loaned.json")
|
|
166
|
-
recs = _create_records(db, most_loaned, Loan, CIRCULATION_LOAN_PID_TYPE)
|
|
167
|
-
|
|
168
|
-
ri = RecordIndexer()
|
|
169
|
-
for rec in recs:
|
|
170
|
-
ri.index(rec)
|
|
171
|
-
|
|
172
|
-
current_search.flush_and_refresh(index="loans")
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
"locations": testdata["locations"],
|
|
176
|
-
"internal_locations": testdata["internal_locations"],
|
|
177
|
-
"documents": testdata["documents"],
|
|
178
|
-
"items": testdata["items"],
|
|
179
|
-
"loans": most_loaned,
|
|
180
|
-
"series": testdata["series"],
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
@pytest.fixture()
|
|
185
|
-
def item_record(app):
|
|
186
|
-
"""Fixture to return an Item payload."""
|
|
187
|
-
return {
|
|
188
|
-
"pid": "itemid-1",
|
|
189
|
-
"document_pid": "docid-1",
|
|
190
|
-
"document": {"$ref": document_ref_builder(app, "itemid-1")},
|
|
191
|
-
"barcode": "123456789-1",
|
|
192
|
-
"title": "Test item x",
|
|
193
|
-
"internal_location_pid": "ilocid-1",
|
|
194
|
-
"internal_location": {"$ref": internal_location_ref_builder(app, "itemid-1")},
|
|
195
|
-
"medium": "NOT_SPECIFIED",
|
|
196
|
-
"status": "CAN_CIRCULATE",
|
|
197
|
-
"circulation_restriction": "NO_RESTRICTION",
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
@pytest.fixture()
|
|
202
|
-
def loan_params():
|
|
203
|
-
"""Params for API REST payload."""
|
|
204
|
-
return dict(
|
|
205
|
-
transaction_user_pid="4",
|
|
206
|
-
patron_pid="1",
|
|
207
|
-
document_pid="docid-1",
|
|
208
|
-
item_pid=dict(type="pitmid", value="itemid-2"),
|
|
209
|
-
transaction_location_pid="locid-1",
|
|
210
|
-
transaction_date="2018-02-01T09:30:00+02:00",
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
@pytest.fixture()
|
|
215
|
-
def bucket(bucket_from_dir):
|
|
216
|
-
"""Create temporary bucket fixture."""
|
|
217
|
-
with tempfile.TemporaryDirectory(prefix="ils-test-") as temp_dir:
|
|
218
|
-
bucket = bucket_from_dir(temp_dir)
|
|
219
|
-
yield bucket
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
@pytest.fixture()
|
|
223
|
-
def with_access(app):
|
|
224
|
-
"""Enable explicit permission check (`_access`)."""
|
|
225
|
-
app.config["ILS_RECORDS_EXPLICIT_PERMISSIONS_ENABLED"] = True
|
|
226
|
-
yield
|
|
227
|
-
app.config["ILS_RECORDS_EXPLICIT_PERMISSIONS_ENABLED"] = False
|