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.
Files changed (115) hide show
  1. invenio_app_ils/__init__.py +1 -1
  2. invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
  3. invenio_app_ils/circulation/api.py +9 -5
  4. invenio_app_ils/circulation/config.py +1 -0
  5. invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +1 -1
  6. invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +2 -2
  7. invenio_app_ils/circulation/notifications/api.py +9 -1
  8. invenio_app_ils/circulation/notifications/messages.py +2 -1
  9. invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
  10. invenio_app_ils/cli.py +92 -64
  11. invenio_app_ils/config.py +10 -2
  12. invenio_app_ils/documents/loaders/jsonschemas/document.py +1 -1
  13. invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +2 -2
  14. invenio_app_ils/patrons/anonymization.py +0 -4
  15. invenio_app_ils/series/loaders/jsonschemas/series.py +1 -1
  16. invenio_app_ils/webpack.py +27 -0
  17. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/METADATA +56 -43
  18. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/RECORD +24 -112
  19. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/entry_points.txt +3 -0
  20. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/top_level.txt +0 -1
  21. invenio_app_ils/notifications/admin.py +0 -57
  22. tests/__init__.py +0 -8
  23. tests/api/__init__.py +0 -8
  24. tests/api/acquisition/__init__.py +0 -8
  25. tests/api/acquisition/test_acq_orders_crud.py +0 -99
  26. tests/api/acquisition/test_acq_orders_permissions.py +0 -104
  27. tests/api/acquisition/test_acq_providers_permissions.py +0 -83
  28. tests/api/circulation/__init__.py +0 -8
  29. tests/api/circulation/test_expired_loans.py +0 -88
  30. tests/api/circulation/test_loan_bulk_extend.py +0 -169
  31. tests/api/circulation/test_loan_checkout.py +0 -422
  32. tests/api/circulation/test_loan_default_durations.py +0 -43
  33. tests/api/circulation/test_loan_document_resolver.py +0 -18
  34. tests/api/circulation/test_loan_extend.py +0 -122
  35. tests/api/circulation/test_loan_item_permissions.py +0 -135
  36. tests/api/circulation/test_loan_item_resolver.py +0 -26
  37. tests/api/circulation/test_loan_list_permissions.py +0 -98
  38. tests/api/circulation/test_loan_patron_resolver.py +0 -38
  39. tests/api/circulation/test_loan_request.py +0 -338
  40. tests/api/circulation/test_loan_update.py +0 -150
  41. tests/api/circulation/test_notifications.py +0 -221
  42. tests/api/conftest.py +0 -227
  43. tests/api/document_requests/__init__.py +0 -8
  44. tests/api/document_requests/test_document_requests.py +0 -131
  45. tests/api/document_requests/test_document_requests_permissions.py +0 -159
  46. tests/api/document_requests/test_notifications_filter.py +0 -35
  47. tests/api/ill/__init__.py +0 -8
  48. tests/api/ill/test_ill_brw_crud.py +0 -74
  49. tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -198
  50. tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -280
  51. tests/api/ill/test_ill_brw_reqs_permissions.py +0 -163
  52. tests/api/ill/test_ill_providers_permissions.py +0 -82
  53. tests/api/ils/__init__.py +0 -8
  54. tests/api/ils/documents/__init__.py +0 -8
  55. tests/api/ils/documents/test_document_crud.py +0 -57
  56. tests/api/ils/documents/test_document_permissions.py +0 -100
  57. tests/api/ils/documents/test_document_resolvers.py +0 -35
  58. tests/api/ils/eitems/__init__.py +0 -8
  59. tests/api/ils/eitems/test_eitems_crud.py +0 -42
  60. tests/api/ils/eitems/test_eitems_permissions.py +0 -85
  61. tests/api/ils/eitems/test_files.py +0 -162
  62. tests/api/ils/items/__init__.py +0 -8
  63. tests/api/ils/items/test_apis.py +0 -43
  64. tests/api/ils/items/test_items_crud.py +0 -99
  65. tests/api/ils/items/test_items_permissions.py +0 -107
  66. tests/api/ils/records_relations/__init__.py +0 -8
  67. tests/api/ils/records_relations/helpers.py +0 -115
  68. tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
  69. tests/api/ils/records_relations/test_records_relations_sequence.py +0 -294
  70. tests/api/ils/records_relations/test_records_relations_siblings.py +0 -751
  71. tests/api/ils/series/__init__.py +0 -8
  72. tests/api/ils/series/test_series_permissions.py +0 -95
  73. tests/api/ils/test_anonymization.py +0 -181
  74. tests/api/ils/test_apis.py +0 -76
  75. tests/api/ils/test_closures.py +0 -353
  76. tests/api/ils/test_errors.py +0 -125
  77. tests/api/ils/test_facets.py +0 -88
  78. tests/api/ils/test_internal_locations.py +0 -96
  79. tests/api/ils/test_loaders.py +0 -51
  80. tests/api/ils/test_metadata_extensions.py +0 -206
  81. tests/api/ils/test_notifications.py +0 -173
  82. tests/api/ils/test_notifications_mails.py +0 -37
  83. tests/api/ils/test_notifications_permissions.py +0 -55
  84. tests/api/ils/test_patrons.py +0 -102
  85. tests/api/ils/test_record_delete.py +0 -42
  86. tests/api/ils/test_record_permissions.py +0 -132
  87. tests/api/ils/test_resolvers.py +0 -205
  88. tests/api/ils/test_stats.py +0 -142
  89. tests/api/ils/test_tasks.py +0 -209
  90. tests/api/ils/test_vocabularies.py +0 -35
  91. tests/conftest.py +0 -221
  92. tests/data/acq_orders.json +0 -110
  93. tests/data/acq_providers.json +0 -12
  94. tests/data/document_requests.json +0 -77
  95. tests/data/documents.json +0 -238
  96. tests/data/eitems.json +0 -71
  97. tests/data/ill_borrowing_requests.json +0 -77
  98. tests/data/ill_providers.json +0 -12
  99. tests/data/internal_locations.json +0 -22
  100. tests/data/items.json +0 -304
  101. tests/data/loans.json +0 -133
  102. tests/data/loans_most_loaned.json +0 -128
  103. tests/data/locations.json +0 -26
  104. tests/data/series.json +0 -33
  105. tests/helpers.py +0 -107
  106. tests/templates/notifications/title_body.html +0 -8
  107. tests/templates/notifications/title_body_html.html +0 -13
  108. tests/templates/notifications/title_body_html_ctx.html +0 -13
  109. tests/templates/notifications/title_only.html +0 -3
  110. tests/test_post_logout_redirect.py +0 -23
  111. tests/test_version.py +0 -17
  112. /tests/templates/notifications/blank.html → /invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
  113. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/WHEEL +0 -0
  114. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/AUTHORS.rst +0 -0
  115. {invenio_app_ils-4.5.0.dist-info → invenio_app_ils-5.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +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
- """Series API Tests."""
@@ -1,95 +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 Series permissions."""
9
-
10
- import json
11
-
12
- from flask import url_for
13
-
14
- from tests.helpers import user_login
15
-
16
- _HTTP_OK = [200, 201, 204]
17
- SERIES_PID = "serid-1"
18
- ITEM_ENDPOINT = "invenio_records_rest.serid_item"
19
- LIST_ENDPOINT = "invenio_records_rest.serid_list"
20
-
21
-
22
- def test_series_read_permissions(client, testdata, json_headers, users):
23
- """Test Series endpoints read permissions."""
24
- tests = [
25
- ("admin", _HTTP_OK),
26
- ("librarian", _HTTP_OK),
27
- ("patron1", _HTTP_OK),
28
- ("anonymous", _HTTP_OK),
29
- ]
30
-
31
- def _test_list(expected_status):
32
- """Test get list."""
33
- url = url_for(LIST_ENDPOINT)
34
- res = client.get(url, headers=json_headers)
35
- assert res.status_code in expected_status
36
-
37
- def _test_read(expected_status, pid):
38
- """Test record read."""
39
- url = url_for(ITEM_ENDPOINT, pid_value=pid)
40
- res = client.get(url, headers=json_headers)
41
- assert res.status_code in expected_status
42
-
43
- for username, expected_status in tests:
44
- user_login(client, username, users)
45
- _test_list(expected_status)
46
- _test_read(expected_status, SERIES_PID)
47
-
48
-
49
- def test_series_edit_permissions(client, testdata, json_headers, users):
50
- """Test series endpoints permissions."""
51
- dummy_series = dict(
52
- title="The Gulf: The Making of An American Sea",
53
- authors=["Einstein, Albert", "Stachel, John J et al."],
54
- mode_of_issuance="MULTIPART_MONOGRAPH",
55
- )
56
- tests = [
57
- ("admin", _HTTP_OK, dummy_series),
58
- ("librarian", _HTTP_OK, dummy_series),
59
- ("patron1", [403], dummy_series),
60
- ("anonymous", [401], dummy_series),
61
- ]
62
-
63
- def _test_create(expected_status, data):
64
- """Test record creation."""
65
- url = url_for(LIST_ENDPOINT)
66
- res = client.post(url, headers=json_headers, data=json.dumps(data))
67
- assert res.status_code in expected_status
68
-
69
- if res.status_code < 400:
70
- record = res.get_json()["metadata"]
71
- assert record
72
- return record["pid"]
73
-
74
- def _test_update(expected_status, data, pid):
75
- """Test record update."""
76
- pid_value = pid or SERIES_PID
77
- url = url_for(ITEM_ENDPOINT, pid_value=pid_value)
78
- res = client.put(url, headers=json_headers, data=json.dumps(data))
79
- assert res.status_code in expected_status
80
- if res.status_code < 400:
81
- record = res.get_json()["metadata"]
82
- assert record
83
-
84
- def _test_delete(expected_status, pid):
85
- """Test record delete."""
86
- pid_value = pid or SERIES_PID
87
- url = url_for(ITEM_ENDPOINT, pid_value=pid_value)
88
- res = client.delete(url, headers=json_headers)
89
- assert res.status_code in expected_status
90
-
91
- for username, expected_status, data in tests:
92
- user_login(client, username, users)
93
- pid = _test_create(expected_status, data)
94
- _test_update(expected_status, data, pid)
95
- _test_delete(expected_status, pid)
@@ -1,181 +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 anonymization of users."""
9
-
10
- from copy import deepcopy
11
-
12
- import pytest
13
- from flask import url_for
14
- from invenio_accounts.models import User
15
- from invenio_circulation.api import Loan
16
- from invenio_circulation.proxies import current_circulation
17
- from invenio_db import db
18
- from invenio_oauthclient.models import RemoteAccount, UserIdentity
19
- from invenio_search import current_search
20
- from invenio_userprofiles.models import UserProfile
21
-
22
- from invenio_app_ils.acquisition.api import Order
23
- from invenio_app_ils.acquisition.search import OrderSearch
24
- from invenio_app_ils.circulation.search import (
25
- get_active_loans_by_patron_pid,
26
- get_loans_by_patron_pid,
27
- )
28
- from invenio_app_ils.document_requests.api import DocumentRequest
29
- from invenio_app_ils.document_requests.search import DocumentRequestSearch
30
- from invenio_app_ils.errors import AnonymizationActiveLoansError
31
- from invenio_app_ils.ill.api import BorrowingRequest
32
- from invenio_app_ils.ill.search import BorrowingRequestsSearch
33
- from invenio_app_ils.patrons.anonymization import anonymize_patron_data
34
- from invenio_app_ils.patrons.api import patron_exists
35
- from tests.helpers import user_login, user_logout
36
-
37
-
38
- def check_user_deleted(user_pid):
39
- """Check if user was deleted from database."""
40
- with db.session.begin_nested():
41
- user_identity = UserIdentity.query.filter(
42
- UserIdentity.id_user == user_pid
43
- ).first()
44
-
45
- assert user_identity is None
46
-
47
- remote_account = RemoteAccount.query.filter(
48
- RemoteAccount.user_id == user_pid
49
- ).first()
50
- assert remote_account is None
51
-
52
- user_profile = UserProfile.query.filter(UserProfile.user_id == user_pid).first()
53
- assert user_profile is None
54
-
55
- user = User.query.filter(User.id == user_pid).first()
56
- assert user is None
57
-
58
-
59
- def check_user_exists(user_pid):
60
- """Check if user exists in database."""
61
- with db.session.begin_nested():
62
- assert patron_exists(user_pid)
63
-
64
-
65
- def check_user_activity(app, user_pid, client, json_headers):
66
- """Check if there are records related to the user."""
67
- # wait ES refresh
68
- current_search.flush_and_refresh(index="*")
69
-
70
- AnonymousPatron = app.config["ILS_PATRON_ANONYMOUS_CLASS"]
71
- anonymous_patron_fields = AnonymousPatron().dumps_loader()
72
-
73
- loans = get_loans_by_patron_pid(user_pid).scan()
74
- for hit in loans:
75
- # test ES
76
- assert hit["patron"] == anonymous_patron_fields
77
- # test DB
78
- loan = Loan.get_record_by_pid(hit.pid)
79
- assert loan["patron"] == anonymous_patron_fields
80
- # test REST
81
- url = url_for("invenio_records_rest.loanid_item", pid_value=hit.pid)
82
- res = client.get(url, headers=json_headers)
83
- assert res.get_json()["metadata"]["patron"] == anonymous_patron_fields
84
-
85
- borrowing_requests = BorrowingRequestsSearch().search_by_patron_pid(user_pid).scan()
86
- for hit in borrowing_requests:
87
- # test ES
88
- assert hit["patron"] == anonymous_patron_fields
89
- # test DB
90
- borrowing_request = BorrowingRequest.get_record_by_pid(hit.pid)
91
- assert borrowing_request["patron"] == anonymous_patron_fields
92
- # test REST
93
- url = url_for("invenio_records_rest.illbid_item", pid_value=hit.pid)
94
- res = client.get(url, headers=json_headers)
95
- assert res.get_json()["metadata"]["patron"] == anonymous_patron_fields
96
-
97
- document_requests = DocumentRequestSearch().search_by_patron_pid(user_pid).scan()
98
- for hit in document_requests:
99
- # test ES
100
- assert hit["patron"] == anonymous_patron_fields
101
- # test DB
102
- document_request = DocumentRequest.get_record_by_pid(hit.pid)
103
- assert document_request["patron"] == anonymous_patron_fields
104
- # test REST
105
- url = url_for("invenio_records_rest.dreqid_item", pid_value=hit.pid)
106
- res = client.get(url, headers=json_headers)
107
- assert res.get_json()["metadata"]["patron"] == anonymous_patron_fields
108
-
109
- acquisitions = OrderSearch().search_by_patron_pid(user_pid).scan()
110
- for hit in acquisitions:
111
- # test ES
112
- assert hit["patron"] == anonymous_patron_fields
113
- # test DB
114
- acquisition = Order.get_record_by_pid(hit.pid)
115
- assert acquisition["patron"] == anonymous_patron_fields
116
- # test REST
117
- url = url_for("invenio_records_rest.acqoid_item", pid_value=hit.pid)
118
- res = client.get(url, headers=json_headers)
119
- assert res.get_json()["metadata"]["patron"] == anonymous_patron_fields
120
-
121
-
122
- def cancel_active_loans(patron_pid, client, users):
123
- """Cancel active loans of a patron."""
124
- user = user_login(client, "admin", users)
125
-
126
- active_loans = get_active_loans_by_patron_pid(patron_pid).scan()
127
-
128
- for hit in active_loans:
129
- loan = Loan.get_record_by_pid(hit.pid)
130
- params = deepcopy(loan)
131
- params.update(
132
- dict(
133
- cancel_reason="Loan cancelled to anonymize user.",
134
- transaction_user_pid=str(user.id),
135
- )
136
- )
137
- current_circulation.circulation.trigger(loan, **dict(params, trigger="cancel"))
138
-
139
- loan.commit()
140
- db.session.commit()
141
- current_circulation.loan_indexer().index(loan)
142
-
143
- current_search.flush_and_refresh(index="*")
144
-
145
-
146
- def test_anonymization(app, client, json_headers, users, testdata):
147
- """Test anonymization of a user."""
148
- # Anonymize patron
149
- patron_pid = users["patron3"].id
150
-
151
- check_user_exists(patron_pid)
152
- cancel_active_loans(patron_pid, client, users)
153
- anonymize_patron_data(patron_pid)
154
- check_user_deleted(patron_pid)
155
-
156
- user_login(client, "admin", users)
157
- check_user_activity(app, patron_pid, client, json_headers)
158
- user_logout(client)
159
-
160
- # Anonymize librarian
161
- librarian_pid = users["librarian"].id
162
-
163
- check_user_exists(librarian_pid)
164
- cancel_active_loans(librarian_pid, client, users)
165
- anonymize_patron_data(librarian_pid)
166
- check_user_deleted(librarian_pid)
167
-
168
- # Anonymize patron while logged in
169
- patron_pid = users["patron2"].id
170
- user_login(client, "patron2", users)
171
-
172
- check_user_exists(patron_pid)
173
- cancel_active_loans(patron_pid, client, users)
174
- anonymize_patron_data(patron_pid)
175
- check_user_deleted(patron_pid)
176
-
177
- # It should fail when anonymizing patron with active loans
178
- patron_pid = users["patron1"].id
179
- check_user_exists(patron_pid)
180
- with pytest.raises(AnonymizationActiveLoansError):
181
- assert anonymize_patron_data(patron_pid)
@@ -1,76 +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
- """Test ILS APIs."""
9
-
10
- import pytest
11
- from flask import url_for
12
- from invenio_accounts.models import User
13
-
14
- from invenio_app_ils.documents.api import Document
15
- from invenio_app_ils.patrons.api import patron_exists
16
- from invenio_app_ils.proxies import current_app_ils
17
- from invenio_app_ils.records.api import IlsRecord
18
- from invenio_app_ils.series.api import Series
19
- from tests.helpers import get_test_record
20
-
21
-
22
- def test_apis(client, json_headers, testdata):
23
- """Test various APIs."""
24
-
25
- def test_patron_exists():
26
- """Test return True if item exists."""
27
- test_patron = User.query.all()[0]
28
- assert patron_exists(int(test_patron.id))
29
- assert patron_exists(str(test_patron.id))
30
-
31
- with pytest.raises(AssertionError):
32
- assert not patron_exists("not a number")
33
- assert not patron_exists("-1")
34
- assert not patron_exists("0")
35
-
36
- def test_get_record_by_pid():
37
- """Test get_record_by_pid."""
38
- tests = [("docid-1", Document), ("serid-1", Series)]
39
-
40
- for pid, cls in tests:
41
- record = cls.get_record_by_pid(pid)
42
-
43
- assert isinstance(record, cls)
44
- assert record["pid"] == pid
45
- assert record._pid_type == cls._pid_type
46
-
47
- def test_get_record_by_pid_and_pid_type():
48
- """Test get_record_by_pid with pid_type."""
49
- tests = [("docid-1", "docid", Document), ("serid-1", "serid", Series)]
50
-
51
- for pid, pid_type, expected_cls in tests:
52
- record = IlsRecord.get_record_by_pid(pid, pid_type=pid_type)
53
-
54
- assert isinstance(record, expected_cls)
55
- assert record["pid"] == pid
56
- assert record._pid_type == pid_type
57
-
58
- def test_get_default_location_pid():
59
- """Asset that the default location is the first created."""
60
- first = get_test_record(testdata, "locations", "locid-1")
61
- pid_value, _ = current_app_ils.get_default_location_pid
62
- assert pid_value == first["pid"]
63
-
64
- def test_no_etag():
65
- """Assert that the response headers do not contain ETag."""
66
- url = url_for("invenio_records_rest.docid_item", pid_value="docid-1")
67
- res = client.get(url, headers=json_headers)
68
- assert res.cache_control.no_cache
69
- assert res.get_etag() == (None, None)
70
- assert "Last-Modified" not in res.headers
71
-
72
- test_patron_exists()
73
- test_get_record_by_pid()
74
- test_get_record_by_pid_and_pid_type()
75
- test_get_default_location_pid()
76
- test_no_etag()