invenio-app-ils 1.0.0a54__py2.py3-none-any.whl → 6.1.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 (331) hide show
  1. invenio_app_ils/__init__.py +2 -2
  2. invenio_app_ils/acquisition/api.py +3 -1
  3. invenio_app_ils/acquisition/config.py +10 -21
  4. invenio_app_ils/acquisition/jsonresolvers/order_order_lines.py +1 -0
  5. invenio_app_ils/acquisition/mappings/os-v1/acq_orders/order-v1.0.0.json +364 -0
  6. invenio_app_ils/acquisition/mappings/os-v2/__init__.py +0 -0
  7. invenio_app_ils/acquisition/mappings/os-v2/acq_orders/order-v1.0.0.json +364 -0
  8. invenio_app_ils/acquisition/mappings/v7/acq_orders/order-v1.0.0.json +14 -0
  9. invenio_app_ils/acquisition/search.py +5 -15
  10. invenio_app_ils/assets/semantic-ui/less/theme.config +103 -0
  11. invenio_app_ils/assets/semantic-ui/templates/.gitkeep +0 -0
  12. invenio_app_ils/circulation/api.py +153 -67
  13. invenio_app_ils/circulation/config.py +29 -25
  14. invenio_app_ils/circulation/indexer.py +14 -19
  15. invenio_app_ils/circulation/loaders/__init__.py +2 -0
  16. invenio_app_ils/circulation/loaders/schemas/json/loan_checkout.py +2 -3
  17. invenio_app_ils/circulation/loaders/schemas/json/loan_request.py +82 -16
  18. invenio_app_ils/circulation/loaders/schemas/json/loan_self_checkout.py +19 -0
  19. invenio_app_ils/circulation/notifications/api.py +23 -19
  20. invenio_app_ils/circulation/notifications/messages.py +6 -6
  21. invenio_app_ils/circulation/notifications/tasks.py +1 -2
  22. invenio_app_ils/circulation/receivers.py +2 -5
  23. invenio_app_ils/circulation/search.py +23 -22
  24. invenio_app_ils/circulation/serializers/__init__.py +2 -2
  25. invenio_app_ils/circulation/serializers/csv.py +1 -3
  26. invenio_app_ils/circulation/serializers/custom_fields.py +5 -9
  27. invenio_app_ils/circulation/serializers/json.py +2 -6
  28. invenio_app_ils/circulation/serializers/response.py +12 -12
  29. invenio_app_ils/circulation/stats/views.py +5 -15
  30. invenio_app_ils/circulation/tasks.py +2 -6
  31. invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/self_checkout.html +19 -0
  32. invenio_app_ils/circulation/templates/invenio_app_ils_circulation/notifications/update_dates.html +19 -0
  33. invenio_app_ils/circulation/utils.py +23 -17
  34. invenio_app_ils/circulation/views.py +103 -39
  35. invenio_app_ils/cli.py +220 -260
  36. invenio_app_ils/closures/api.py +72 -7
  37. invenio_app_ils/closures/serializers/__init__.py +16 -0
  38. invenio_app_ils/closures/serializers/response.py +35 -0
  39. invenio_app_ils/closures/serializers/schema.py +28 -0
  40. invenio_app_ils/closures/tasks.py +4 -11
  41. invenio_app_ils/closures/views.py +66 -0
  42. invenio_app_ils/config.py +207 -150
  43. invenio_app_ils/document_requests/api.py +6 -21
  44. invenio_app_ils/document_requests/loaders/__init__.py +4 -12
  45. invenio_app_ils/document_requests/mappings/{v6 → os-v1}/__init__.py +2 -2
  46. invenio_app_ils/document_requests/mappings/os-v1/document_requests/document_request-v1.0.0.json +196 -0
  47. invenio_app_ils/document_requests/mappings/os-v2/__init__.py +8 -0
  48. invenio_app_ils/document_requests/mappings/os-v2/document_requests/document_request-v1.0.0.json +196 -0
  49. invenio_app_ils/document_requests/notifications/api.py +2 -7
  50. invenio_app_ils/document_requests/notifications/messages.py +3 -7
  51. invenio_app_ils/document_requests/search.py +2 -6
  52. invenio_app_ils/document_requests/views.py +4 -10
  53. invenio_app_ils/documents/api.py +19 -25
  54. invenio_app_ils/documents/indexer.py +13 -20
  55. invenio_app_ils/documents/jsonresolvers/document_circulation.py +11 -33
  56. invenio_app_ils/documents/jsonresolvers/document_eitem.py +1 -0
  57. invenio_app_ils/documents/jsonresolvers/document_item.py +8 -9
  58. invenio_app_ils/documents/jsonresolvers/document_stock.py +7 -7
  59. invenio_app_ils/documents/loaders/jsonschemas/document.py +3 -14
  60. invenio_app_ils/documents/mappings/{v6 → os-v1}/__init__.py +1 -1
  61. invenio_app_ils/documents/mappings/os-v1/documents/document-v1.0.0.json +932 -0
  62. invenio_app_ils/documents/mappings/os-v1/documents/document-v2.0.0.json +1066 -0
  63. invenio_app_ils/documents/mappings/os-v2/__init__.py +8 -0
  64. invenio_app_ils/documents/mappings/os-v2/documents/document-v1.0.0.json +932 -0
  65. invenio_app_ils/documents/mappings/os-v2/documents/document-v2.0.0.json +1066 -0
  66. invenio_app_ils/documents/mappings/v7/documents/document-v1.0.0.json +102 -11
  67. invenio_app_ils/documents/mappings/v7/documents/document-v2.0.0.json +1066 -0
  68. invenio_app_ils/documents/schemas/documents/document-v2.0.0.json +732 -0
  69. invenio_app_ils/eitems/api.py +41 -7
  70. invenio_app_ils/eitems/loaders/jsonschemas/eitems.py +6 -6
  71. invenio_app_ils/eitems/mappings/{v6 → os-v1}/__init__.py +1 -1
  72. invenio_app_ils/eitems/mappings/os-v1/eitems/eitem-v1.0.0.json +139 -0
  73. invenio_app_ils/eitems/mappings/os-v1/eitems/eitem-v2.0.0.json +142 -0
  74. invenio_app_ils/eitems/mappings/os-v1/eitems/eitem-v3.0.0.json +179 -0
  75. invenio_app_ils/{series/mappings/v6 → eitems/mappings/os-v2}/__init__.py +1 -1
  76. invenio_app_ils/eitems/mappings/os-v2/eitems/eitem-v1.0.0.json +139 -0
  77. invenio_app_ils/eitems/mappings/os-v2/eitems/eitem-v2.0.0.json +142 -0
  78. invenio_app_ils/eitems/mappings/os-v2/eitems/eitem-v3.0.0.json +179 -0
  79. invenio_app_ils/eitems/mappings/v7/eitems/eitem-v1.0.0.json +12 -2
  80. invenio_app_ils/eitems/mappings/v7/eitems/eitem-v2.0.0.json +142 -0
  81. invenio_app_ils/eitems/mappings/v7/eitems/eitem-v3.0.0.json +179 -0
  82. invenio_app_ils/eitems/schemas/eitems/eitem-v2.0.0.json +120 -0
  83. invenio_app_ils/eitems/schemas/eitems/eitem-v3.0.0.json +120 -0
  84. invenio_app_ils/eitems/search.py +3 -9
  85. invenio_app_ils/errors.py +119 -6
  86. invenio_app_ils/ext.py +15 -18
  87. invenio_app_ils/facets.py +13 -15
  88. invenio_app_ils/files/receivers.py +1 -1
  89. invenio_app_ils/files/views.py +1 -2
  90. invenio_app_ils/ill/api.py +1 -2
  91. invenio_app_ils/ill/config.py +10 -16
  92. invenio_app_ils/ill/jsonresolvers/borrowing_request_document.py +2 -3
  93. invenio_app_ils/ill/jsonresolvers/borrowing_request_patron.py +1 -3
  94. invenio_app_ils/ill/jsonresolvers/borrowing_request_provider.py +1 -3
  95. invenio_app_ils/ill/loaders/__init__.py +3 -9
  96. invenio_app_ils/ill/loaders/jsonschemas/patron_loan_actions.py +1 -8
  97. invenio_app_ils/ill/loaders/jsonschemas/patron_loan_extension_actions.py +3 -8
  98. invenio_app_ils/ill/mappings/{v6 → os-v1}/__init__.py +1 -1
  99. invenio_app_ils/ill/mappings/os-v1/ill_borrowing_requests/borrowing_request-v1.0.0.json +254 -0
  100. invenio_app_ils/{acquisition/mappings/v6 → ill/mappings/os-v2}/__init__.py +1 -1
  101. invenio_app_ils/ill/mappings/os-v2/ill_borrowing_requests/borrowing_request-v1.0.0.json +254 -0
  102. invenio_app_ils/ill/mappings/v7/ill_borrowing_requests/borrowing_request-v1.0.0.json +14 -0
  103. invenio_app_ils/ill/notifications/api.py +1 -4
  104. invenio_app_ils/ill/search.py +3 -9
  105. invenio_app_ils/ill/serializers/__init__.py +1 -3
  106. invenio_app_ils/ill/views.py +2 -4
  107. invenio_app_ils/internal_locations/api.py +1 -4
  108. invenio_app_ils/internal_locations/mappings/{v6 → os-v1}/__init__.py +1 -1
  109. invenio_app_ils/internal_locations/mappings/os-v1/internal_locations/internal_location-v1.0.0.json +108 -0
  110. invenio_app_ils/internal_locations/mappings/os-v2/__init__.py +8 -0
  111. invenio_app_ils/internal_locations/mappings/os-v2/internal_locations/internal_location-v1.0.0.json +108 -0
  112. invenio_app_ils/internal_locations/search.py +1 -3
  113. invenio_app_ils/items/api.py +18 -15
  114. invenio_app_ils/items/indexer.py +1 -3
  115. invenio_app_ils/items/jsonresolvers/item_document.py +1 -1
  116. invenio_app_ils/items/jsonresolvers/item_internal_location.py +2 -6
  117. invenio_app_ils/items/loaders/jsonschemas/items.py +2 -0
  118. {tests/api/circulation → invenio_app_ils/items/mappings/os-v1}/__init__.py +1 -1
  119. invenio_app_ils/items/mappings/os-v1/items/item-v1.0.0.json +285 -0
  120. invenio_app_ils/items/mappings/os-v1/items/item-v2.0.0.json +326 -0
  121. {tests/api/ill → invenio_app_ils/items/mappings/os-v2}/__init__.py +1 -1
  122. invenio_app_ils/items/mappings/os-v2/items/item-v1.0.0.json +285 -0
  123. invenio_app_ils/items/mappings/os-v2/items/item-v2.0.0.json +326 -0
  124. invenio_app_ils/items/mappings/v7/items/item-v1.0.0.json +11 -0
  125. invenio_app_ils/items/mappings/v7/items/item-v2.0.0.json +326 -0
  126. invenio_app_ils/items/schemas/items/item-v1.0.0.json +22 -0
  127. invenio_app_ils/items/schemas/items/item-v2.0.0.json +172 -0
  128. invenio_app_ils/items/search.py +18 -11
  129. invenio_app_ils/items/serializers/item.py +7 -6
  130. invenio_app_ils/literature/api.py +1 -3
  131. invenio_app_ils/literature/search.py +3 -5
  132. invenio_app_ils/literature/serializers/csv.py +1 -3
  133. invenio_app_ils/literature/serializers/json.py +1 -3
  134. invenio_app_ils/locations/api.py +2 -4
  135. invenio_app_ils/locations/indexer.py +3 -9
  136. invenio_app_ils/locations/loaders/__init__.py +1 -3
  137. invenio_app_ils/locations/loaders/jsonschemas/location.py +4 -11
  138. invenio_app_ils/locations/mappings/{v6 → os-v1}/__init__.py +1 -1
  139. invenio_app_ils/locations/mappings/os-v1/locations/location-v1.0.0.json +90 -0
  140. invenio_app_ils/locations/mappings/os-v2/__init__.py +8 -0
  141. invenio_app_ils/locations/mappings/os-v2/locations/location-v1.0.0.json +90 -0
  142. invenio_app_ils/notifications/messages.py +4 -3
  143. invenio_app_ils/notifications/tasks.py +1 -3
  144. invenio_app_ils/notifications/views.py +2 -2
  145. invenio_app_ils/patrons/anonymization.py +53 -32
  146. invenio_app_ils/patrons/api.py +3 -5
  147. invenio_app_ils/patrons/cli.py +1 -2
  148. invenio_app_ils/patrons/indexer.py +4 -13
  149. invenio_app_ils/patrons/mappings/os-v1/__init__.py +8 -0
  150. invenio_app_ils/patrons/mappings/os-v1/patrons/patron-v1.0.0.json +42 -0
  151. invenio_app_ils/patrons/mappings/os-v2/__init__.py +8 -0
  152. invenio_app_ils/patrons/mappings/os-v2/patrons/patron-v1.0.0.json +42 -0
  153. invenio_app_ils/patrons/views.py +4 -14
  154. invenio_app_ils/permissions.py +93 -35
  155. invenio_app_ils/providers/api.py +2 -6
  156. invenio_app_ils/providers/config.py +11 -19
  157. invenio_app_ils/providers/indexer.py +2 -6
  158. invenio_app_ils/providers/mappings/{v6 → os-v1}/__init__.py +1 -1
  159. invenio_app_ils/providers/mappings/os-v1/providers/provider-v1.0.0.json +57 -0
  160. invenio_app_ils/providers/mappings/os-v2/__init__.py +8 -0
  161. invenio_app_ils/providers/mappings/os-v2/providers/provider-v1.0.0.json +57 -0
  162. invenio_app_ils/providers/mappings/v7/__init__.py +1 -1
  163. invenio_app_ils/providers/proxies.py +1 -3
  164. invenio_app_ils/records/api.py +3 -9
  165. invenio_app_ils/records/loaders/schemas/changed_by.py +1 -3
  166. invenio_app_ils/records/loaders/schemas/identifiers.py +23 -0
  167. invenio_app_ils/records/metadata_extensions.py +1 -4
  168. invenio_app_ils/records/permissions.py +8 -2
  169. invenio_app_ils/records/serializers/__init__.py +1 -4
  170. invenio_app_ils/records/views.py +4 -7
  171. invenio_app_ils/records_relations/api.py +11 -34
  172. invenio_app_ils/records_relations/indexer.py +1 -3
  173. invenio_app_ils/records_relations/retriever.py +14 -9
  174. invenio_app_ils/records_relations/views.py +16 -50
  175. invenio_app_ils/relations/api.py +17 -16
  176. invenio_app_ils/search_permissions.py +24 -24
  177. invenio_app_ils/series/api.py +10 -12
  178. invenio_app_ils/series/loaders/jsonschemas/series.py +3 -2
  179. invenio_app_ils/{items/mappings/v6 → series/mappings/os-v1}/__init__.py +1 -1
  180. invenio_app_ils/series/mappings/os-v1/series/series-v1.0.0.json +324 -0
  181. invenio_app_ils/series/mappings/os-v1/series/series-v2.0.0.json +396 -0
  182. {tests/api/acquisition → invenio_app_ils/series/mappings/os-v2}/__init__.py +1 -1
  183. invenio_app_ils/series/mappings/os-v2/series/series-v1.0.0.json +324 -0
  184. invenio_app_ils/series/mappings/os-v2/series/series-v2.0.0.json +396 -0
  185. invenio_app_ils/series/mappings/v7/series/series-v1.0.0.json +35 -3
  186. invenio_app_ils/series/mappings/v7/series/series-v2.0.0.json +396 -0
  187. invenio_app_ils/series/schemas/series/series-v2.0.0.json +354 -0
  188. invenio_app_ils/stats/aggregations/aggr_file_download/os-v1/aggr-file-download-v1.json +60 -0
  189. invenio_app_ils/stats/aggregations/aggr_file_download/os-v2/__init__.py +9 -0
  190. invenio_app_ils/stats/aggregations/aggr_file_download/os-v2/aggr-file-download-v1.json +60 -0
  191. invenio_app_ils/stats/event_builders.py +73 -0
  192. invenio_app_ils/stats/file_download/os-v1/file-download-v1.json +72 -0
  193. invenio_app_ils/stats/file_download/os-v2/__init__.py +9 -0
  194. invenio_app_ils/stats/file_download/os-v2/file-download-v1.json +72 -0
  195. invenio_app_ils/stats/processors.py +30 -0
  196. invenio_app_ils/stats/templates/aggregations/__init__.py +8 -0
  197. invenio_app_ils/stats/templates/aggregations/ils_record_changes/__init__.py +8 -0
  198. invenio_app_ils/stats/templates/aggregations/ils_record_changes/os-v2/__init__.py +8 -0
  199. invenio_app_ils/stats/templates/aggregations/ils_record_changes/os-v2/aggr-ils-record-changes-v1.json +41 -0
  200. {tests → invenio_app_ils/stats/templates/events}/__init__.py +2 -2
  201. invenio_app_ils/stats/templates/events/ils_record_changes/__init__.py +8 -0
  202. invenio_app_ils/stats/templates/events/ils_record_changes/os-v2/__init__.py +8 -0
  203. invenio_app_ils/stats/templates/events/ils_record_changes/os-v2/ils-record-changes-v1.json +41 -0
  204. invenio_app_ils/templates/logged_out.html +12 -0
  205. invenio_app_ils/views.py +18 -4
  206. invenio_app_ils/vocabularies/api.py +1 -1
  207. invenio_app_ils/vocabularies/cli.py +6 -18
  208. invenio_app_ils/vocabularies/data/acq_order_line_payment_modes.json +5 -0
  209. invenio_app_ils/vocabularies/data/identifier_schemes.json +5 -0
  210. invenio_app_ils/vocabularies/indexer.py +1 -4
  211. invenio_app_ils/vocabularies/jsonresolvers/licenses.py +2 -11
  212. invenio_app_ils/vocabularies/mappings/os-v1/__init__.py +8 -0
  213. invenio_app_ils/vocabularies/mappings/os-v1/vocabularies/vocabulary-v1.0.0.json +23 -0
  214. invenio_app_ils/vocabularies/mappings/os-v2/__init__.py +8 -0
  215. invenio_app_ils/vocabularies/mappings/os-v2/vocabularies/vocabulary-v1.0.0.json +23 -0
  216. invenio_app_ils/vocabularies/sources/base.py +1 -3
  217. invenio_app_ils/webpack.py +27 -0
  218. invenio_app_ils-6.1.0.dist-info/METADATA +627 -0
  219. {invenio_app_ils-1.0.0a54.dist-info → invenio_app_ils-6.1.0.dist-info}/RECORD +227 -249
  220. {invenio_app_ils-1.0.0a54.dist-info → invenio_app_ils-6.1.0.dist-info}/WHEEL +1 -1
  221. {invenio_app_ils-1.0.0a54.dist-info → invenio_app_ils-6.1.0.dist-info}/entry_points.txt +9 -5
  222. {invenio_app_ils-1.0.0a54.dist-info → invenio_app_ils-6.1.0.dist-info}/top_level.txt +0 -1
  223. invenio_app_ils/acquisition/mappings/v6/acq_orders/order-v1.0.0.json +0 -352
  224. invenio_app_ils/document_requests/mappings/v6/document_requests/document_request-v1.0.0.json +0 -199
  225. invenio_app_ils/documents/mappings/v6/documents/document-v1.0.0.json +0 -843
  226. invenio_app_ils/eitems/mappings/v6/eitems/eitem-v1.0.0.json +0 -131
  227. invenio_app_ils/ill/mappings/v6/ill_borrowing_requests/borrowing_request-v1.0.0.json +0 -242
  228. invenio_app_ils/internal_locations/mappings/v6/internal_locations/internal_location-v1.0.0.json +0 -110
  229. invenio_app_ils/items/mappings/v6/items/item-v1.0.0.json +0 -276
  230. invenio_app_ils/locations/mappings/v6/locations/location-v1.0.0.json +0 -92
  231. invenio_app_ils/notifications/admin.py +0 -57
  232. invenio_app_ils/patrons/mappings/v6/__init__.py +0 -8
  233. invenio_app_ils/patrons/mappings/v6/patrons/patron-v1.0.0.json +0 -44
  234. invenio_app_ils/providers/mappings/v6/providers/provider-v1.0.0.json +0 -59
  235. invenio_app_ils/series/mappings/v6/series/series-v1.0.0.json +0 -298
  236. invenio_app_ils/stats/aggregations/aggr_file_download/v6/aggr-file-download-v1.json +0 -59
  237. invenio_app_ils/stats/file_download/v6/file-download-v1.json +0 -74
  238. invenio_app_ils/version.py +0 -14
  239. invenio_app_ils/vocabularies/mappings/v6/__init__.py +0 -8
  240. invenio_app_ils/vocabularies/mappings/v6/vocabularies/vocabulary-v1.0.0.json +0 -25
  241. invenio_app_ils-1.0.0a54.dist-info/METADATA +0 -95
  242. tests/api/__init__.py +0 -8
  243. tests/api/acquisition/test_acq_orders_crud.py +0 -102
  244. tests/api/acquisition/test_acq_orders_permissions.py +0 -104
  245. tests/api/acquisition/test_acq_providers_permissions.py +0 -83
  246. tests/api/circulation/test_expired_loans.py +0 -88
  247. tests/api/circulation/test_loan_checkout.py +0 -216
  248. tests/api/circulation/test_loan_default_durations.py +0 -57
  249. tests/api/circulation/test_loan_document_resolver.py +0 -18
  250. tests/api/circulation/test_loan_extend.py +0 -132
  251. tests/api/circulation/test_loan_item_permissions.py +0 -139
  252. tests/api/circulation/test_loan_item_resolver.py +0 -26
  253. tests/api/circulation/test_loan_list_permissions.py +0 -104
  254. tests/api/circulation/test_loan_patron_resolver.py +0 -38
  255. tests/api/circulation/test_loan_request.py +0 -234
  256. tests/api/circulation/test_loan_update.py +0 -150
  257. tests/api/circulation/test_notifications.py +0 -229
  258. tests/api/conftest.py +0 -236
  259. tests/api/document_requests/__init__.py +0 -8
  260. tests/api/document_requests/test_document_requests.py +0 -131
  261. tests/api/document_requests/test_document_requests_permissions.py +0 -177
  262. tests/api/document_requests/test_notifications_filter.py +0 -36
  263. tests/api/ill/test_ill_brw_crud.py +0 -84
  264. tests/api/ill/test_ill_brw_reqs_patron_loan_create_action.py +0 -207
  265. tests/api/ill/test_ill_brw_reqs_patron_loan_extension_actions.py +0 -298
  266. tests/api/ill/test_ill_brw_reqs_permissions.py +0 -169
  267. tests/api/ill/test_ill_providers_permissions.py +0 -82
  268. tests/api/ils/__init__.py +0 -8
  269. tests/api/ils/documents/__init__.py +0 -8
  270. tests/api/ils/documents/test_document_crud.py +0 -57
  271. tests/api/ils/documents/test_document_permissions.py +0 -106
  272. tests/api/ils/documents/test_document_resolvers.py +0 -35
  273. tests/api/ils/eitems/__init__.py +0 -8
  274. tests/api/ils/eitems/test_eitems_crud.py +0 -41
  275. tests/api/ils/eitems/test_eitems_permissions.py +0 -84
  276. tests/api/ils/eitems/test_files.py +0 -172
  277. tests/api/ils/items/__init__.py +0 -8
  278. tests/api/ils/items/test_apis.py +0 -43
  279. tests/api/ils/items/test_items_crud.py +0 -60
  280. tests/api/ils/items/test_items_permissions.py +0 -107
  281. tests/api/ils/items/test_items_update.py +0 -47
  282. tests/api/ils/records_relations/__init__.py +0 -8
  283. tests/api/ils/records_relations/helpers.py +0 -115
  284. tests/api/ils/records_relations/test_records_relations_parentchild.py +0 -560
  285. tests/api/ils/records_relations/test_records_relations_sequence.py +0 -302
  286. tests/api/ils/records_relations/test_records_relations_siblings.py +0 -678
  287. tests/api/ils/series/__init__.py +0 -8
  288. tests/api/ils/series/test_series_permissions.py +0 -95
  289. tests/api/ils/test_anonymization.py +0 -189
  290. tests/api/ils/test_apis.py +0 -76
  291. tests/api/ils/test_closures.py +0 -324
  292. tests/api/ils/test_errors.py +0 -126
  293. tests/api/ils/test_facets.py +0 -84
  294. tests/api/ils/test_internal_locations.py +0 -96
  295. tests/api/ils/test_loaders.py +0 -55
  296. tests/api/ils/test_metadata_extensions.py +0 -205
  297. tests/api/ils/test_notifications.py +0 -183
  298. tests/api/ils/test_notifications_mails.py +0 -39
  299. tests/api/ils/test_notifications_permissions.py +0 -55
  300. tests/api/ils/test_patrons.py +0 -106
  301. tests/api/ils/test_record_delete.py +0 -46
  302. tests/api/ils/test_record_permissions.py +0 -135
  303. tests/api/ils/test_resolvers.py +0 -207
  304. tests/api/ils/test_stats.py +0 -160
  305. tests/api/ils/test_tasks.py +0 -211
  306. tests/api/ils/test_vocabularies.py +0 -35
  307. tests/conftest.py +0 -123
  308. tests/data/acq_orders.json +0 -89
  309. tests/data/acq_providers.json +0 -12
  310. tests/data/document_requests.json +0 -77
  311. tests/data/documents.json +0 -237
  312. tests/data/eitems.json +0 -66
  313. tests/data/ill_borrowing_requests.json +0 -77
  314. tests/data/ill_providers.json +0 -12
  315. tests/data/internal_locations.json +0 -22
  316. tests/data/items.json +0 -271
  317. tests/data/loans.json +0 -133
  318. tests/data/loans_most_loaned.json +0 -128
  319. tests/data/locations.json +0 -20
  320. tests/data/series.json +0 -33
  321. tests/helpers.py +0 -104
  322. tests/templates/notifications/title_body.html +0 -8
  323. tests/templates/notifications/title_body_html.html +0 -13
  324. tests/templates/notifications/title_body_html_ctx.html +0 -13
  325. tests/templates/notifications/title_only.html +0 -3
  326. tests/test_version.py +0 -17
  327. /tests/templates/notifications/blank.html → /invenio_app_ils/acquisition/mappings/os-v1/__init__.py +0 -0
  328. /invenio_app_ils/stats/aggregations/aggr_file_download/{v6 → os-v1}/__init__.py +0 -0
  329. /invenio_app_ils/stats/file_download/{v6 → os-v1}/__init__.py +0 -0
  330. {invenio_app_ils-1.0.0a54.dist-info → invenio_app_ils-6.1.0.dist-info/licenses}/AUTHORS.rst +0 -0
  331. {invenio_app_ils-1.0.0a54.dist-info → invenio_app_ils-6.1.0.dist-info/licenses}/LICENSE +0 -0
@@ -21,6 +21,8 @@ from marshmallow import (
21
21
  validates_schema,
22
22
  )
23
23
 
24
+ from invenio_app_ils.proxies import current_app_ils
25
+
24
26
  from .base import LoanBaseSchemaV1
25
27
 
26
28
 
@@ -31,14 +33,54 @@ def request_start_date_default():
31
33
 
32
34
  def request_expire_date_default():
33
35
  """Set default value for request_expire_date field."""
34
- duration_days = current_app.config[
35
- "ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"
36
- ]
36
+ duration_days = current_app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"]
37
37
  duration = timedelta(days=duration_days)
38
38
  now = arrow.get().utcnow()
39
39
  return (now + duration).date().isoformat()
40
40
 
41
41
 
42
+ def get_offset_duration(request_start_date, location_pid, working_days_to_offset):
43
+ location = current_app_ils.location_record_cls.get_record_by_pid(location_pid)
44
+
45
+ weekdays = location["opening_weekdays"]
46
+ exceptions = location["opening_exceptions"]
47
+ disabled = set()
48
+
49
+ date = request_start_date
50
+ max_date = request_start_date + timedelta(
51
+ days=current_app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"]
52
+ )
53
+ # Calculate all disabled days that need to be skipped
54
+ # Used in calculating non-closure days to offset in post-load checks
55
+ while date <= max_date:
56
+ date_iso = date.date().isoformat()
57
+ is_open = weekdays[date.weekday()]["is_open"]
58
+
59
+ for exception in exceptions:
60
+ start_date = arrow.get(exception["start_date"])
61
+ end_date = arrow.get(exception["end_date"])
62
+
63
+ if start_date <= arrow.get(date.date()) <= end_date:
64
+ is_open = exception["is_open"]
65
+
66
+ if not is_open:
67
+ disabled.add(date_iso)
68
+
69
+ date += timedelta(days=1)
70
+
71
+ total_offset_days = 0
72
+ date = request_start_date
73
+ while working_days_to_offset > 0:
74
+ date_iso = date.date().isoformat()
75
+ if date_iso not in disabled:
76
+ disabled.add(date_iso)
77
+ working_days_to_offset -= 1
78
+ date += timedelta(days=1)
79
+ total_offset_days += 1
80
+
81
+ return timedelta(days=total_offset_days)
82
+
83
+
42
84
  class LoanRequestDeliverySchemaV1(Schema):
43
85
  """Loan common delivery Schema."""
44
86
 
@@ -65,12 +107,12 @@ class LoanRequestSchemaV1(LoanBaseSchemaV1):
65
107
  """Loan request schema."""
66
108
 
67
109
  delivery = fields.Nested(LoanRequestDeliverySchemaV1)
68
- request_expire_date = DateString(missing=request_expire_date_default)
69
- request_start_date = DateString(missing=request_start_date_default)
110
+ request_expire_date = DateString(load_default=request_expire_date_default)
111
+ request_start_date = DateString(load_default=request_start_date_default)
70
112
 
71
113
  @validates_schema()
72
114
  def validates_schema(self, data, **kwargs):
73
- """Validate schema delivery field."""
115
+ """Validate schema fields."""
74
116
  delivery = data.get("delivery")
75
117
  # if delivery methods is configured, it has to be a mandatory field
76
118
  if (
@@ -79,29 +121,44 @@ class LoanRequestSchemaV1(LoanBaseSchemaV1):
79
121
  ):
80
122
  raise ValidationError("Delivery is required.", "delivery")
81
123
 
124
+ if current_app.config["ILS_CIRCULATION_LOAN_REQUEST_OFFSET"] < 0:
125
+ raise ValidationError(
126
+ {
127
+ "ILS_CIRCULATION_LOAN_REQUEST_OFFSET": [
128
+ "The minimum days after which a loan can start cannot be negative."
129
+ ]
130
+ }
131
+ )
132
+
82
133
  @post_load()
83
134
  def postload_checks(self, data, **kwargs):
84
135
  """Validate dates values."""
85
136
  start = arrow.get(data["request_start_date"]).date()
86
137
  end = arrow.get(data["request_expire_date"]).date()
87
- duration_days = current_app.config[
88
- "ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"
89
- ]
138
+ duration_days = current_app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"]
90
139
  duration = timedelta(days=duration_days)
91
140
 
141
+ loan_request_offset = current_app.config["ILS_CIRCULATION_LOAN_REQUEST_OFFSET"]
142
+ offset_with_closures = get_offset_duration(
143
+ arrow.get(data["request_start_date"]),
144
+ data["transaction_location_pid"],
145
+ loan_request_offset,
146
+ )
147
+
92
148
  if end < start:
93
149
  raise ValidationError(
94
150
  {
95
- "request_start_date": "The request start date "
96
- "cannot be after the end date.",
97
- "request_end_date": "The request end date "
98
- "cannot be before the start date.",
151
+ "request_start_date": (
152
+ "The request start date cannot be after the end date."
153
+ ),
154
+ "request_end_date": (
155
+ "The request end date cannot be before the start date."
156
+ ),
99
157
  }
100
158
  )
101
159
  elif end - start > duration:
102
- message = (
103
- "The request duration "
104
- + "cannot be longer than {} days.".format(duration_days)
160
+ message = "The request duration cannot be longer than {} days.".format(
161
+ duration_days
105
162
  )
106
163
  raise ValidationError(
107
164
  {
@@ -109,4 +166,13 @@ class LoanRequestSchemaV1(LoanBaseSchemaV1):
109
166
  "request_expire_date": [message],
110
167
  }
111
168
  )
169
+ elif end - start < offset_with_closures:
170
+ message = "The requested start date must be at least {} non-closure days from today.".format(
171
+ loan_request_offset
172
+ )
173
+ raise ValidationError(
174
+ {
175
+ "request_start_date": [message],
176
+ }
177
+ )
112
178
  return data
@@ -0,0 +1,19 @@
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
6
+ # it under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Invenio App ILS circulation Loan Checkout loader JSON schema."""
9
+
10
+ from invenio_circulation.records.loaders.schemas.json import LoanItemPIDSchemaV1
11
+ from marshmallow import fields
12
+
13
+ from .base import LoanBaseSchemaV1
14
+
15
+
16
+ class LoanSelfCheckoutSchemaV1(LoanBaseSchemaV1):
17
+ """Loan self-checkout schema."""
18
+
19
+ item_pid = fields.Nested(LoanItemPIDSchemaV1, required=True)
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2021 CERN.
3
+ # Copyright (C) 2021-2025 CERN.
4
4
  #
5
5
  # invenio-app-ils is free software; you can redistribute it and/or modify it
6
6
  # under the terms of the MIT License; see LICENSE file for more details.
@@ -10,10 +10,7 @@
10
10
  from flask import current_app
11
11
  from invenio_records_rest.utils import obj_or_import_string
12
12
 
13
- from invenio_app_ils.notifications.api import (
14
- build_common_msg_ctx,
15
- send_notification,
16
- )
13
+ from invenio_app_ils.notifications.api import build_common_msg_ctx, send_notification
17
14
  from invenio_app_ils.patrons.api import Patron
18
15
 
19
16
 
@@ -36,9 +33,7 @@ def send_loan_notification(loan, action, msg_extra_ctx=None, **kwargs):
36
33
  msg_ctx.update(build_common_msg_ctx(loan))
37
34
  msg_ctx.update(_build_circulation_msg_ctx())
38
35
 
39
- func_or_path = current_app.config[
40
- "ILS_CIRCULATION_NOTIFICATIONS_MSG_BUILDER"
41
- ]
36
+ func_or_path = current_app.config["ILS_CIRCULATION_NOTIFICATIONS_MSG_BUILDER"]
42
37
  builder = obj_or_import_string(func_or_path)
43
38
  msg = builder(loan, action, msg_ctx, **kwargs)
44
39
 
@@ -69,12 +64,22 @@ def send_expiring_loan_reminder_notification(loan, expiring_in_days):
69
64
  )
70
65
 
71
66
 
72
- def send_bulk_extend_notification(extended_loans,
73
- not_extended_loans,
74
- patron_pid,
75
- action="bulk_extend",
76
- msg_extra_ctx=None,
77
- **kwargs):
67
+ def send_dates_updated_notification(loan):
68
+ """Send reminder notification."""
69
+ send_loan_notification(
70
+ action="update_dates",
71
+ loan=loan,
72
+ )
73
+
74
+
75
+ def send_bulk_extend_notification(
76
+ extended_loans,
77
+ not_extended_loans,
78
+ patron_pid,
79
+ action="bulk_extend",
80
+ msg_extra_ctx=None,
81
+ **kwargs
82
+ ):
78
83
  """Send notification to patron about bulk extend action."""
79
84
  _filter = current_app.config["ILS_CIRCULATION_NOTIFICATIONS_FILTER"]
80
85
  if _filter and _filter({}, action, **kwargs) is False:
@@ -92,13 +97,12 @@ def send_bulk_extend_notification(extended_loans,
92
97
  context.update(build_common_msg_ctx(loan))
93
98
  not_extended_loans_ctx.append(context)
94
99
 
95
- msg_ctx.update(extended_loans=extended_loans_ctx,
96
- not_extended_loans_ctx=not_extended_loans_ctx)
100
+ msg_ctx.update(
101
+ extended_loans=extended_loans_ctx, not_extended_loans_ctx=not_extended_loans_ctx
102
+ )
97
103
  msg_ctx.update(_build_circulation_msg_ctx())
98
104
 
99
- func_or_path = current_app.config[
100
- "ILS_CIRCULATION_NOTIFICATIONS_MSG_BUILDER"
101
- ]
105
+ func_or_path = current_app.config["ILS_CIRCULATION_NOTIFICATIONS_MSG_BUILDER"]
102
106
  builder = obj_or_import_string(func_or_path)
103
107
  msg = builder({}, action, msg_ctx, **kwargs)
104
108
 
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2021 CERN.
3
+ # Copyright (C) 2021-2025 CERN.
4
4
  #
5
5
  # invenio-app-ils is free software; you can redistribute it and/or modify it
6
6
  # under the terms of the MIT License; see LICENSE file for more details.
@@ -25,12 +25,14 @@ class NotificationLoanMsg(NotificationMsg):
25
25
  request="request.html",
26
26
  request_no_items="request_no_items.html",
27
27
  checkout="checkout.html",
28
+ self_checkout="self_checkout.html",
28
29
  checkin="checkin.html",
29
30
  extend="extend.html",
30
31
  cancel="cancel.html",
32
+ update_dates="update_dates.html",
31
33
  overdue_reminder="overdue_reminder.html",
32
34
  expiring_reminder="will_expire_in_reminder.html",
33
- bulk_extend="bulk_extend.html"
35
+ bulk_extend="bulk_extend.html",
34
36
  )
35
37
 
36
38
  def __init__(self, loan, action, msg_ctx, **kwargs):
@@ -67,9 +69,7 @@ class NotificationLoanMsg(NotificationMsg):
67
69
  """Get the template filename based on the loan action."""
68
70
  new_state = self.loan["state"]
69
71
  document_pid = self.loan.get("document_pid")
70
- is_request = (
71
- new_state in current_app.config["CIRCULATION_STATES_LOAN_REQUEST"]
72
- )
72
+ is_request = new_state in current_app.config["CIRCULATION_STATES_LOAN_REQUEST"]
73
73
  if (
74
74
  is_request
75
75
  and document_pid
@@ -112,6 +112,6 @@ class NotificationBulkExtendLoanMsg(NotificationLoanMsg):
112
112
 
113
113
  def notification_loan_msg_builder(loan, action, msg_ctx, **kwargs):
114
114
  """Factory builder to create a notification msg."""
115
- if action == 'bulk_extend':
115
+ if action == "bulk_extend":
116
116
  return NotificationBulkExtendLoanMsg(action, msg_ctx, **kwargs)
117
117
  return NotificationLoanMsg(loan, action, msg_ctx, **kwargs)
@@ -27,8 +27,7 @@ celery_logger = get_task_logger(__name__)
27
27
  @shared_task
28
28
  def send_overdue_loans_notification_reminder():
29
29
  """Send notification message for loans that are overdue every X days."""
30
- days = current_app.config[
31
- "ILS_CIRCULATION_NOTIFICATION_OVERDUE_REMINDER_INTERVAL"]
30
+ days = current_app.config["ILS_CIRCULATION_NOTIFICATION_OVERDUE_REMINDER_INTERVAL"]
32
31
  for hit in get_all_overdue_loans().scan():
33
32
  loan = hit.to_dict()
34
33
  days_ago = circulation_overdue_loan_days(loan)
@@ -10,9 +10,7 @@
10
10
  from invenio_circulation.proxies import current_circulation
11
11
  from invenio_circulation.signals import loan_replace_item, loan_state_changed
12
12
 
13
- from invenio_app_ils.circulation.notifications.api import (
14
- send_loan_notification,
15
- )
13
+ from invenio_app_ils.circulation.notifications.api import send_loan_notification
16
14
  from invenio_app_ils.circulation.utils import resolve_item_from_loan
17
15
  from invenio_app_ils.ill.api import BORROWING_REQUEST_PID_TYPE
18
16
  from invenio_app_ils.ill.proxies import current_ils_ill
@@ -53,8 +51,7 @@ def index_after_loan_replace_item(_, old_item_pid, new_item_pid):
53
51
  indexer.index(rec)
54
52
 
55
53
 
56
- def send_notification_after_loan_change(_, initial_loan, loan, trigger,
57
- **kwargs):
54
+ def send_notification_after_loan_change(_, initial_loan, loan, trigger, **kwargs):
58
55
  """Send email notification when the loan changes."""
59
56
  send_notification = kwargs.get("send_notification", True)
60
57
  if send_notification:
@@ -9,10 +9,10 @@
9
9
 
10
10
  from datetime import datetime, timedelta
11
11
 
12
- from elasticsearch_dsl import A, Q
13
12
  from flask import current_app
14
13
  from invenio_circulation.proxies import current_circulation
15
14
  from invenio_circulation.search.api import search_by_pid
15
+ from invenio_search.engine import dsl
16
16
 
17
17
  from invenio_app_ils.items.api import ITEM_PID_TYPE
18
18
 
@@ -21,9 +21,7 @@ def get_loan_next_available_date(document_pid):
21
21
  """Return active loans on document sorted by the earliest end date."""
22
22
  return search_by_pid(
23
23
  document_pid=document_pid,
24
- filter_states=current_app.config.get(
25
- "CIRCULATION_STATES_LOAN_ACTIVE", []
26
- ),
24
+ filter_states=current_app.config.get("CIRCULATION_STATES_LOAN_ACTIVE", []),
27
25
  sort_by_field="end_date",
28
26
  )
29
27
 
@@ -32,9 +30,7 @@ def get_active_loan_by_item_pid(item_pid):
32
30
  """Return any active loans for the given item."""
33
31
  return search_by_pid(
34
32
  item_pid=item_pid,
35
- filter_states=current_app.config.get(
36
- "CIRCULATION_STATES_LOAN_ACTIVE", []
37
- ),
33
+ filter_states=current_app.config.get("CIRCULATION_STATES_LOAN_ACTIVE", []),
38
34
  )
39
35
 
40
36
 
@@ -69,12 +65,19 @@ def get_all_range_expiring_loans_by_patron_pid(expiring_in_days, patron_pid):
69
65
  )
70
66
 
71
67
 
72
- def get_all_range_expiring_ils_loans_by_patron_pid(expiring_in_days,
73
- patron_pid):
74
- """Return ILS loans expiring in the next <days> days for given patron."""
75
- return get_all_range_expiring_loans_by_patron_pid(
76
- expiring_in_days, patron_pid)\
68
+ def get_all_expiring_or_overdue_loans_by_patron_pid(patron_pid):
69
+ """Return ILS loans expiring or overdue for given patron."""
70
+ days = current_app.config["ILS_CIRCULATION_LOAN_WILL_EXPIRE_DAYS"]
71
+ future = (datetime.today() + timedelta(days=days)).date()
72
+ states = current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
73
+ search_cls = current_circulation.loan_search_cls
74
+ return (
75
+ search_cls()
76
+ .filter("term", patron_pid=patron_pid)
77
77
  .filter("term", item_pid__type=ITEM_PID_TYPE)
78
+ .filter("range", end_date=dict(lte="{}||/d".format(future)))
79
+ .filter("terms", state=states)
80
+ )
78
81
 
79
82
 
80
83
  def get_all_expired_loans():
@@ -103,9 +106,9 @@ def get_all_overdue_loans():
103
106
  def get_overdue_loans_by_doc_pid(document_pid):
104
107
  """Return any overdue loans for the given document."""
105
108
  states = current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
106
- return search_by_pid(
107
- document_pid=document_pid, filter_states=states
108
- ).filter("range", end_date=dict(lt="now/d"))
109
+ return search_by_pid(document_pid=document_pid, filter_states=states).filter(
110
+ "range", end_date=dict(lt="now/d")
111
+ )
109
112
 
110
113
 
111
114
  def get_most_loaned_documents(from_date, to_date, bucket_size):
@@ -124,13 +127,13 @@ def get_most_loaned_documents(from_date, to_date, bucket_size):
124
127
  search = search_cls().query(
125
128
  "bool",
126
129
  must=[
127
- Q("terms", state=states),
128
- Q("range", start_date=dict(gte=from_date, lte=to_date)),
130
+ dsl.Q("terms", state=states),
131
+ dsl.Q("range", start_date=dict(gte=from_date, lte=to_date)),
129
132
  ],
130
133
  )
131
134
 
132
135
  # Aggregation with sub-aggregation to calculate the extension count sum
133
- aggs = A("terms", field="document_pid", size=bucket_size)
136
+ aggs = dsl.A("terms", field="document_pid", size=bucket_size)
134
137
  aggs = aggs.metric("extensions", "sum", field="extension_count")
135
138
  search.aggs.bucket("most_loaned_documents", aggs)
136
139
 
@@ -151,7 +154,7 @@ def get_loans_aggregated_by_states(document_pid, states, patron_pid=None):
151
154
  if patron_pid:
152
155
  search = search.filter("term", patron_pid=patron_pid)
153
156
  # Aggregation
154
- aggs = A("terms", field="state")
157
+ aggs = dsl.A("terms", field="state")
155
158
  search.aggs.bucket("states", aggs)
156
159
 
157
160
  return search
@@ -166,9 +169,7 @@ def get_loans_by_patron_pid(patron_pid):
166
169
  def get_active_loans_by_patron_pid(patron_pid):
167
170
  """Returns a search for all the active loans of a given patron."""
168
171
  active_states = current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
169
- search = get_loans_by_patron_pid(patron_pid).filter(
170
- "terms", state=active_states
171
- )
172
+ search = get_loans_by_patron_pid(patron_pid).filter("terms", state=active_states)
172
173
  return search
173
174
 
174
175
 
@@ -6,6 +6,7 @@
6
6
  # under the terms of the MIT License; see LICENSE file for more details.
7
7
 
8
8
  """Loan serializers."""
9
+
9
10
  from invenio_records_rest.serializers.response import search_responsify
10
11
 
11
12
  from invenio_app_ils.records.schemas.json import ILSRecordSchemaJSONV1
@@ -23,5 +24,4 @@ json_v1 = LoanJSONSerializer(ILSRecordSchemaJSONV1, replace_refs=True)
23
24
  json_v1_response = record_responsify_no_etag(json_v1, "application/json")
24
25
  json_v1_search = search_responsify(json_v1, "application/json")
25
26
 
26
- bulk_extend_v1_response = bulk_loan_extend_responsify(json_v1,
27
- 'application/json')
27
+ bulk_extend_v1_response = bulk_loan_extend_responsify(json_v1, "application/json")
@@ -31,9 +31,7 @@ class LoanCSVSerializer(CSVSerializer):
31
31
  field_transaction_user(loan["metadata"])
32
32
  return loan
33
33
 
34
- def transform_search_hit(
35
- self, pid, record_hit, links_factory=None, **kwargs
36
- ):
34
+ def transform_search_hit(self, pid, record_hit, links_factory=None, **kwargs):
37
35
  """Transform search result hit into an intermediate representation."""
38
36
  hit = super().transform_search_hit(
39
37
  pid, record_hit, links_factory=links_factory, **kwargs
@@ -11,6 +11,7 @@ from flask import current_app
11
11
  from invenio_pidstore.errors import PIDDeletedError, PIDDoesNotExistError
12
12
 
13
13
  from invenio_app_ils.circulation.utils import circulation_overdue_loan_days
14
+ from invenio_app_ils.errors import PatronNotFoundError
14
15
  from invenio_app_ils.proxies import current_app_ils
15
16
  from invenio_app_ils.records.jsonresolvers.api import pick
16
17
 
@@ -19,8 +20,7 @@ def field_is_overdue(metadata):
19
20
  """Calculate if the loan is overdue."""
20
21
  metadata["is_overdue"] = False
21
22
  is_loan_active = (
22
- metadata["state"]
23
- in current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
23
+ metadata["state"] in current_app.config["CIRCULATION_STATES_LOAN_ACTIVE"]
24
24
  )
25
25
  if is_loan_active and "end_date" in metadata:
26
26
  metadata["is_overdue"] = circulation_overdue_loan_days(metadata) > 0
@@ -50,13 +50,9 @@ def field_transaction_location(metadata):
50
50
  return
51
51
  Location = current_app_ils.location_record_cls
52
52
  try:
53
- transaction_location = Location.get_record_by_pid(
54
- transaction_location_pid
55
- )
53
+ transaction_location = Location.get_record_by_pid(transaction_location_pid)
56
54
  except PIDDeletedError:
57
- metadata["transaction_location"] = {
58
- "name": "This location was deleted."
59
- }
55
+ metadata["transaction_location"] = {"name": "This location was deleted."}
60
56
  return
61
57
  except PIDDoesNotExistError:
62
58
  metadata["transaction_location"] = {"name": "Location PID invalid."}
@@ -72,6 +68,6 @@ def field_transaction_user(metadata):
72
68
  Patron = current_app_ils.patron_cls
73
69
  try:
74
70
  transaction_user = Patron.get_patron(transaction_user_pid)
75
- except PIDDeletedError:
71
+ except (PIDDeletedError, PatronNotFoundError):
76
72
  return
77
73
  metadata["transaction_user"] = pick(transaction_user, "name")
@@ -9,9 +9,7 @@
9
9
 
10
10
  from invenio_records_rest.serializers.json import JSONSerializer
11
11
 
12
- from invenio_app_ils.literature.serializers.custom_fields import (
13
- field_cover_metadata,
14
- )
12
+ from invenio_app_ils.literature.serializers.custom_fields import field_cover_metadata
15
13
 
16
14
  from .custom_fields import (
17
15
  field_is_overdue,
@@ -36,9 +34,7 @@ class LoanJSONSerializer(JSONSerializer):
36
34
  field_cover_metadata(loan["metadata"].get("document", {}))
37
35
  return loan
38
36
 
39
- def transform_search_hit(
40
- self, pid, record_hit, links_factory=None, **kwargs
41
- ):
37
+ def transform_search_hit(self, pid, record_hit, links_factory=None, **kwargs):
42
38
  """Transform search result hit into an intermediate representation."""
43
39
  hit = super().transform_search_hit(
44
40
  pid, record_hit, links_factory=links_factory, **kwargs
@@ -6,34 +6,34 @@
6
6
  # under the terms of the MIT License; see LICENSE file for more details.
7
7
 
8
8
  """Response serializers for circulation module."""
9
+
9
10
  import json
10
11
 
11
12
  from flask import current_app
12
13
 
13
14
 
14
- def bulk_loan_extend_responsify(serializer,
15
- mimetype):
15
+ def bulk_loan_extend_responsify(serializer, mimetype):
16
16
  """Bulk loan extend serializer.
17
17
 
18
18
  :param schema_class: Serializer instance.
19
19
  :param mimetype: MIME type of response.
20
20
  """
21
21
 
22
- def view(extended_loans, not_extended_loans,
23
- code=200, headers=None, **kwargs):
22
+ def view(extended_loans, not_extended_loans, code=200, headers=None, **kwargs):
24
23
  """Generate the response object."""
25
- response_data = {"extended_loans": [],
26
- "not_extended_loans": []}
24
+ response_data = {"extended_loans": [], "not_extended_loans": []}
27
25
  for loan in extended_loans:
28
- response_data["extended_loans"]\
29
- .append(serializer.serialize(loan["pid"], loan))
26
+ response_data["extended_loans"].append(
27
+ serializer.serialize(loan["pid"], loan)
28
+ )
30
29
  for loan in not_extended_loans:
31
- response_data["not_extended_loans"]\
32
- .append(serializer.serialize(loan["pid"], loan))
30
+ response_data["not_extended_loans"].append(
31
+ serializer.serialize(loan["pid"], loan)
32
+ )
33
33
 
34
34
  response = current_app.response_class(
35
- json.dumps(response_data),
36
- mimetype=mimetype)
35
+ json.dumps(response_data), mimetype=mimetype
36
+ )
37
37
  response.status_code = code
38
38
 
39
39
  if headers is not None:
@@ -16,10 +16,7 @@ from invenio_rest import ContentNegotiatedMethodView
16
16
 
17
17
  from invenio_app_ils.circulation.stats.api import fetch_most_loaned_documents
18
18
  from invenio_app_ils.config import RECORDS_REST_MAX_RESULT_WINDOW
19
- from invenio_app_ils.documents.api import (
20
- DOCUMENT_PID_FETCHER,
21
- DOCUMENT_PID_TYPE,
22
- )
19
+ from invenio_app_ils.documents.api import DOCUMENT_PID_FETCHER, DOCUMENT_PID_TYPE
23
20
  from invenio_app_ils.errors import InvalidParameterError
24
21
  from invenio_app_ils.permissions import need_permissions
25
22
 
@@ -29,13 +26,10 @@ def create_most_loaned_documents_view(blueprint, app):
29
26
  endpoints = app.config.get("RECORDS_REST_ENDPOINTS", [])
30
27
  document_endpoint = endpoints.get(DOCUMENT_PID_TYPE, {})
31
28
  default_media_type = document_endpoint.get("default_media_type", "")
32
- search_serializers_aliases = document_endpoint.get(
33
- "search_serializers_aliases", ""
34
- )
29
+ search_serializers_aliases = document_endpoint.get("search_serializers_aliases", "")
35
30
  search_serializers = document_endpoint.get("search_serializers")
36
31
  serializers = {
37
- mime: obj_or_import_string(func)
38
- for mime, func in search_serializers.items()
32
+ mime: obj_or_import_string(func) for mime, func in search_serializers.items()
39
33
  }
40
34
 
41
35
  view_class = MostLoanedDocumentsResource
@@ -54,9 +48,7 @@ def create_most_loaned_documents_view(blueprint, app):
54
48
 
55
49
  def create_circulation_stats_blueprint(app):
56
50
  """Add statistics views to the blueprint."""
57
- blueprint = Blueprint(
58
- "invenio_app_ils_circulation_stats", __name__, url_prefix=""
59
- )
51
+ blueprint = Blueprint("invenio_app_ils_circulation_stats", __name__, url_prefix="")
60
52
 
61
53
  create_most_loaned_documents_view(blueprint, app)
62
54
 
@@ -134,9 +126,7 @@ class MostLoanedDocumentsResource(ContentNegotiatedMethodView):
134
126
  """
135
127
  size = self._validate_bucket_size()
136
128
  from_date, to_date = self._validate_start_date_range()
137
- most_loaned_documents = fetch_most_loaned_documents(
138
- from_date, to_date, size
139
- )
129
+ most_loaned_documents = fetch_most_loaned_documents(from_date, to_date, size)
140
130
  return self.make_response(
141
131
  pid_fetcher=current_pidstore.fetchers[DOCUMENT_PID_FETCHER],
142
132
  search_result=most_loaned_documents,
@@ -29,9 +29,7 @@ def cancel_expired_loan_requests():
29
29
 
30
30
  for hit in get_all_expired_loans().scan():
31
31
  loan = Loan.get_record_by_pid(hit.pid)
32
- duration_days = current_app.config[
33
- "ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"
34
- ]
32
+ duration_days = current_app.config["ILS_CIRCULATION_LOAN_REQUEST_DURATION_DAYS"]
35
33
  params = deepcopy(loan)
36
34
  params.update(
37
35
  dict(
@@ -40,9 +38,7 @@ def cancel_expired_loan_requests():
40
38
  transaction_user_pid=system_agent_id,
41
39
  )
42
40
  )
43
- current_circulation.circulation.trigger(
44
- loan, **dict(params, trigger="cancel")
45
- )
41
+ current_circulation.circulation.trigger(loan, **dict(params, trigger="cancel"))
46
42
 
47
43
  loan.commit()
48
44
  db.session.commit()
@@ -0,0 +1,19 @@
1
+ {% block title %}
2
+ InvenioILS: loan started for "{{ document.title|safe }}"
3
+ {% endblock %}
4
+
5
+ {% block body_plain %}
6
+ Dear {{ patron.name }},
7
+
8
+ your self-checkout loan for "{{ document.full_title }}" <{{ spa_routes.HOST }}{{ spa_routes.PATHS['literature']|format(pid=document.pid) }}> has started.
9
+
10
+ The due date is {{ loan.end_date }}.
11
+ {% endblock %}
12
+
13
+ {% block body_html %}
14
+ Dear {{ patron.name }}, <br/><br/>
15
+
16
+ your self-checkout loan for <a href="{{ spa_routes.HOST }}{{ spa_routes.PATHS['literature']|format(pid=document.pid) }}">"{{ document.full_title }}"</a> has <b>started</b>. <br/><br/>
17
+
18
+ <b>The due date is {{ loan.end_date }}</b>.<br/>
19
+ {% endblock %}