diff --git a/data/role_policies.json b/data/role_policies.json
index 9994bd0d26..f6b2f2dc87 100644
--- a/data/role_policies.json
+++ b/data/role_policies.json
@@ -680,39 +680,48 @@
],
"stat-access": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat-search": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat-read": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat_cfg-access": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat_cfg-search": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat_cfg-read": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat_cfg-create": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat_cfg-update": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"stat_cfg-delete": [
"pro_full_permissions",
- "pro_statistic_manager"
+ "pro_statistic_manager",
+ "pro_library_administrator"
],
"tmpl-access": [
"pro_full_permissions",
@@ -831,14 +840,17 @@
],
"locent-update": [
"pro_full_permissions",
- "pro_entity_manager"
+ "pro_entity_manager",
+ "pro_library_administrator"
],
"locent-create": [
"pro_full_permissions",
- "pro_entity_manager"
+ "pro_entity_manager",
+ "pro_library_administrator"
],
"locent-delete": [
"pro_full_permissions",
- "pro_entity_manager"
+ "pro_entity_manager",
+ "pro_library_administrator"
]
}
diff --git a/data/stats_cfg.json b/data/stats_cfg.json
index d9e911d282..5ee161365b 100644
--- a/data/stats_cfg.json
+++ b/data/stats_cfg.json
@@ -1,7 +1,7 @@
[
{
"pid": "1",
- "name": "organisation 1, no distributions",
+ "name": "library 1, no distributions",
"description": "Statistics configuration with no distributions",
"category": {
"type": "catalogue",
@@ -9,15 +9,15 @@
"type": "number_of_documents"
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/1"
},
"frequency": "month",
"is_active": true
},
{
"pid": "2",
- "name": "organisation 1, circulation, number of checkouts, time range month",
+ "name": "library 1, circulation, number of checkouts, time range month",
"description": "Statistics configuration with 1 distribution",
"category": {
"type": "circulation",
@@ -28,15 +28,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/1"
},
"frequency": "month",
"is_active": true
},
{
"pid": "3",
- "name": "organisation 1, number of checkouts, library",
+ "name": "library 2, number of checkouts, library",
"description": "Statistics configuration with 1 distribution",
"category": {
"type": "circulation",
@@ -47,15 +47,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/2"
},
"frequency": "month",
"is_active": true
},
{
"pid": "4",
- "name": "organisation 1, 2 distributions",
+ "name": "library 2, 2 distributions",
"description": "Statistics configuration with 2 distributions",
"category": {
"type": "circulation",
@@ -67,15 +67,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/2"
},
"frequency": "month",
"is_active": true
},
{
"pid": "5",
- "name": "organisation 1, 2 distributions, period 1 year",
+ "name": "library 3, 2 distributions, period 1 year",
"description": "Statistics configuration with 2 distributions",
"category": {
"type": "circulation",
@@ -88,15 +88,15 @@
"period": "year"
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/4"
},
"frequency": "month",
"is_active": true
},
{
"pid": "6",
- "name": "organisation 1, 2 distributions, status disabled",
+ "name": "library 3, 2 distributions, status disabled",
"description": "Statistics configuration with 2 distributions",
"category": {
"type": "circulation",
@@ -108,15 +108,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/4"
},
"frequency": "month",
"is_active": false
},
{
"pid": "7",
- "name": "organisation 2, no distributions",
+ "name": "library 5, no distributions",
"description": "Statistics configuration with no distributions",
"category": {
"type": "catalogue",
@@ -124,15 +124,15 @@
"type": "number_of_documents"
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/5"
},
"frequency": "month",
"is_active": true
},
{
"pid": "8",
- "name": "organisation 2, 1 distribution",
+ "name": "library 5, 1 distribution",
"description": "Statistics configuration with 1 distribution",
"category": {
"type": "circulation",
@@ -143,15 +143,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/5"
},
"frequency": "month",
"is_active": true
},
{
"pid": "9",
- "name": "organisation 2, 1 distribution",
+ "name": "library 5, 1 distribution",
"description": "Statistics configuration with 1 distribution",
"category": {
"type": "circulation",
@@ -162,15 +162,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/5"
},
"frequency": "month",
"is_active": true
},
{
"pid": "10",
- "name": "organisation 2, 2 distributions",
+ "name": "library 5, 2 distributions",
"description": "Statistics configuration with 2 distributions",
"category": {
"type": "circulation",
@@ -182,15 +182,15 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/5"
},
"frequency": "month",
"is_active": true
},
{
"pid": "11",
- "name": "organisation 2, 2 distributions, period 1 year",
+ "name": "library 5, 2 distributions, period 1 year",
"description": "Statistics configuration with 2 distributions",
"category": {
"type": "circulation",
@@ -203,15 +203,15 @@
"period": "year"
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/5"
},
"frequency": "month",
"is_active": true
},
{
"pid": "12",
- "name": "organisation 2, 2 distributions, status disabled",
+ "name": "library 5, 2 distributions, status disabled",
"description": "Statistics configuration with 1 distribution",
"category": {
"type": "circulation",
@@ -223,10 +223,10 @@
]
}
},
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/5"
},
"frequency": "month",
"is_active": false
}
-]
+]
\ No newline at end of file
diff --git a/data/users.json b/data/users.json
index 1c0c6270f0..19a568c201 100644
--- a/data/users.json
+++ b/data/users.json
@@ -160,7 +160,8 @@
"pro_read_only",
"pro_catalog_manager",
"pro_circulation_manager",
- "pro_user_manager"
+ "pro_user_manager",
+ "pro_statistic_manager"
],
"libraries": [
{
diff --git a/rero_ils/config.py b/rero_ils/config.py
index b5ff221dec..995e3f056d 100644
--- a/rero_ils/config.py
+++ b/rero_ils/config.py
@@ -839,10 +839,12 @@ def _(x):
'json': 'application/json'
},
search_serializers_aliases={
- 'json': 'application/json'
+ 'json': 'application/json',
+ 'rero+json': 'application/json',
},
search_serializers={
- 'application/json': 'rero_ils.modules.serializers:json_v1_search'
+ 'application/json': 'rero_ils.modules.serializers:json_v1_search',
+ 'application/rero+json': 'rero_ils.modules.stats_cfg.serializers:json_search'
},
list_route='/stats_cfg/',
record_loaders={
@@ -2336,11 +2338,23 @@ def _(x):
field='category.type',
size=RERO_ILS_AGGREGATION_SIZE.get(
'stats_cfg', RERO_ILS_DEFAULT_AGGREGATION_SIZE)
+ ),
+ aggs=dict(
+ indicator=dict(terms=dict(field='category.indicator.type', size=DOCUMENTS_AGGREGATION_SIZE))
)
- )
+ ),
+ frequency=dict(
+ terms=dict(field='frequency', size=DOCUMENTS_AGGREGATION_SIZE),
+
+ ),
+ library=dict(terms=dict(field='library.pid', size=RERO_ILS_DEFAULT_AGGREGATION_SIZE))
),
filters={
- _('category'): and_term_filter('category.type')
+ _('category'): and_term_filter('category.type'),
+ _('indicator'): and_term_filter('category.indicator.type'),
+ _('frequency'): and_term_filter('frequency'),
+ _('library'): and_term_filter('library.pid'),
+ _('active'): and_term_filter('is_active')
}
)
)
@@ -2909,7 +2923,7 @@ def _(x):
template='rero_ils/detailed_view_stats.html',
record_class='rero_ils.modules.stats.api.api:Stat',
view_imp='rero_ils.modules.stats.views.stats_view_method',
- permission_factory_imp='rero_ils.permissions.admin_permission_factory',
+ permission_factory_imp='rero_ils.modules.stats.permissions:stats_ui_permission_factory',
)
}
diff --git a/rero_ils/modules/libraries/api.py b/rero_ils/modules/libraries/api.py
index f83385b913..33aa81e481 100644
--- a/rero_ils/modules/libraries/api.py
+++ b/rero_ils/modules/libraries/api.py
@@ -24,6 +24,7 @@
import pytz
from dateutil import parser
from dateutil.rrule import FREQNAMES, rrule
+from elasticsearch_dsl import Q
from flask_babelex import gettext as _
from rero_ils.modules.api import IlsRecord, IlsRecordsIndexer, IlsRecordsSearch
@@ -31,6 +32,7 @@
from rero_ils.modules.locations.api import LocationsSearch
from rero_ils.modules.minters import id_minter
from rero_ils.modules.providers import Provider
+from rero_ils.modules.stats_cfg.api import StatsConfigurationSearch
from rero_ils.modules.users.models import UserRole
from rero_ils.modules.utils import date_string_to_utc, \
extracted_data_from_ref, sorted_pids, strtotime
@@ -377,6 +379,11 @@ def get_links_to_me(self, get_pids=False):
AcqReceiptsSearch
from rero_ils.modules.patrons.api import PatronsSearch
links = {}
+ stat_cfg_query = StatsConfigurationSearch()\
+ .filter(
+ Q('term', library__pid=self.pid) |
+ Q('term', filter_by_libraries__pid=self.pid)
+ )
location_query = LocationsSearch() \
.filter('term', library__pid=self.pid)
patron_query = PatronsSearch() \
@@ -388,16 +395,20 @@ def get_links_to_me(self, get_pids=False):
locations = sorted_pids(location_query)
librarians = sorted_pids(patron_query)
receipts = sorted_pids(receipt_query)
+ stats_cfg = sorted_pids(stat_cfg_query)
else:
locations = location_query.count()
librarians = patron_query.count()
receipts = receipt_query.count()
+ stats_cfg = stat_cfg_query.count()
if locations:
links['locations'] = locations
if librarians:
links['patrons'] = librarians
if receipts:
links['acq_receipts'] = receipts
+ if stats_cfg:
+ links['stats_cfg'] = stats_cfg
return links
def reasons_not_to_delete(self):
diff --git a/rero_ils/modules/operation_logs/es_templates/v7/operation_logs.json b/rero_ils/modules/operation_logs/es_templates/v7/operation_logs.json
index c12da2580c..0348be84f0 100644
--- a/rero_ils/modules/operation_logs/es_templates/v7/operation_logs.json
+++ b/rero_ils/modules/operation_logs/es_templates/v7/operation_logs.json
@@ -195,7 +195,12 @@
"type": "keyword"
},
"location_name": {
- "type": "text"
+ "type": "text",
+ "fields": {
+ "raw": {
+ "type": "keyword"
+ }
+ }
}
}
},
diff --git a/rero_ils/modules/stats/api/indicators/__init__.py b/rero_ils/modules/stats/api/indicators/__init__.py
index 34045cbd53..76f4a9c09b 100644
--- a/rero_ils/modules/stats/api/indicators/__init__.py
+++ b/rero_ils/modules/stats/api/indicators/__init__.py
@@ -22,3 +22,4 @@
from .circulation import *
from .others import *
from .patron import *
+from .requests import *
diff --git a/rero_ils/modules/stats/api/indicators/circulation.py b/rero_ils/modules/stats/api/indicators/circulation.py
index 4fc2ed2fd7..b0f2125d11 100644
--- a/rero_ils/modules/stats/api/indicators/circulation.py
+++ b/rero_ils/modules/stats/api/indicators/circulation.py
@@ -118,6 +118,11 @@ def aggregation(self, distribution):
'terms',
field='loan.item.library_pid',
size=self.cfg.aggs_size
+ ),
+ 'owning_location': A(
+ 'terms',
+ field='loan.item.holding.location_name.raw',
+ size=self.cfg.aggs_size
)
}
return cfg[distribution]
@@ -141,6 +146,7 @@ def label(self, distribution, bucket):
'patron_postal_code': lambda: bucket.key,
'transaction_channel': lambda: bucket.key,
'owning_library': lambda:
- f'{self.cfg.libraries[bucket.key]} ({bucket.key})'
+ f'{self.cfg.libraries[bucket.key]} ({bucket.key})',
+ 'owning_location': lambda: bucket.key,
}
return cfg[distribution]()
diff --git a/rero_ils/modules/stats/api/indicators/others.py b/rero_ils/modules/stats/api/indicators/others.py
index a3bf26d677..df0a5927f3 100644
--- a/rero_ils/modules/stats/api/indicators/others.py
+++ b/rero_ils/modules/stats/api/indicators/others.py
@@ -55,7 +55,7 @@ def aggregation(self, distribution):
:returns: an elasticsearch aggregation object
"""
cfg = {
- 'library': A(
+ 'owning_library': A(
'terms',
field='holdings.organisation.library_pid',
size=self.cfg.aggs_size,
@@ -91,7 +91,7 @@ def label(self, distribution, bucket):
:rtype: str
"""
cfg = {
- 'library': lambda:
+ 'owning_library': lambda:
f'{self.cfg.libraries[bucket.key]} ({bucket.key})',
'created_month': lambda: bucket.key_as_string,
'created_year': lambda: bucket.key_as_string,
@@ -124,7 +124,7 @@ def aggregation(self, distribution):
:returns: an elasticsearch aggregation object
"""
cfg = {
- 'library': A(
+ 'owning_library': A(
'terms',
field='library.pid',
size=self.cfg.aggs_size,
@@ -154,7 +154,7 @@ def label(self, distribution, bucket):
:rtype: str
"""
cfg = {
- 'library': lambda:
+ 'owning_library': lambda:
f'{self.cfg.libraries[bucket.key]} ({bucket.key})',
'created_month': lambda: bucket.key_as_string,
'created_year': lambda: bucket.key_as_string
@@ -185,13 +185,13 @@ def aggregation(self, distribution):
:returns: an elasticsearch aggregation object
"""
cfg = {
- 'library': A(
+ 'owning_library': A(
'terms',
field='library.pid',
size=self.cfg.aggs_size,
include=self.cfg.lib_pids
),
- 'location': A(
+ 'owning_location': A(
'terms',
field='location.pid',
size=self.cfg.aggs_size,
@@ -202,6 +202,16 @@ def aggregation(self, distribution):
field='type',
size=self.cfg.aggs_size
),
+ 'document_type': A(
+ 'terms',
+ field='document.document_type.main_type',
+ size=self.cfg.aggs_size
+ ),
+ 'document_subtype': A(
+ 'terms',
+ field='document.document_type.subtype',
+ size=self.cfg.aggs_size
+ ),
'created_month': A(
'date_histogram',
field='_created',
@@ -226,11 +236,13 @@ def label(self, distribution, bucket):
:rtype: str
"""
cfg = {
- 'library': lambda:
+ 'owning_library': lambda:
f'{self.cfg.libraries[bucket.key]} ({bucket.key})',
- 'location': lambda:
+ 'owning_location': lambda:
f'{self.cfg.locations[bucket.key]} ({bucket.key})',
'type': lambda: bucket.key,
+ 'document_type': lambda: bucket.key,
+ 'document_subtype': lambda: bucket.key,
'created_month': lambda: bucket.key_as_string,
'created_year': lambda: bucket.key_as_string
}
@@ -265,7 +277,7 @@ def aggregation(self, distribution):
:returns: an elasticsearch aggregation object
"""
cfg = {
- 'library': A(
+ 'owning_library': A(
'terms',
field='record.library_pid',
size=self.cfg.aggs_size
@@ -294,7 +306,7 @@ def label(self, distribution, bucket):
:rtype: str
"""
cfg = {
- 'library': lambda:
+ 'owning_library': lambda:
f'{self.cfg.libraries[bucket.key]} ({bucket.key})',
'action_month': lambda: bucket.key_as_string,
'action_year': lambda: bucket.key_as_string
diff --git a/rero_ils/modules/stats/api/indicators/requests.py b/rero_ils/modules/stats/api/indicators/requests.py
new file mode 100644
index 0000000000..01e6e65eed
--- /dev/null
+++ b/rero_ils/modules/stats/api/indicators/requests.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+#
+# RERO ILS
+# Copyright (C) 2019-2023 RERO
+# Copyright (C) 2019-2023 UCLouvain
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""Circulation Requests Indicator Report Configuration."""
+
+
+from elasticsearch_dsl.aggs import A
+
+from .circulation import NumberOfCirculationCfg
+
+
+class NumberOfRequestsCfg(NumberOfCirculationCfg):
+ """Number of circulation action based on trigger."""
+
+ def aggregation(self, distribution):
+ """Elasticsearch Aggregation configuration to compute distributions.
+
+ :param distrubtion: str - report distrubtion name
+ :returns: an elasticsearch aggregation object
+ """
+ cfg = {
+ 'pickup_location': A(
+ 'terms',
+ field='loan.pickup_location.pid',
+ size=self.cfg.aggs_size
+ )
+ }
+ if agg := cfg.get(distribution):
+ return agg
+ return super().aggregation(distribution)
+
+ def label(self, distribution, bucket):
+ """Column/Raw label transformations.
+
+ :param distrubtion: str - the report distrubtion name
+ :param bucket: the elasticsearch aggregation bucket
+ :returns: the label
+ :rtype: str
+ """
+ cfg = {
+ 'pickup_location': lambda:
+ f'{self.cfg.locations[bucket.key]} ({bucket.key})'
+ }
+ if label_fn := cfg.get(distribution):
+ return label_fn()
+ return super().label(distribution=distribution, bucket=bucket)
diff --git a/rero_ils/modules/stats/api/report.py b/rero_ils/modules/stats/api/report.py
index 3d89f9cb3f..a85ad58327 100644
--- a/rero_ils/modules/stats/api/report.py
+++ b/rero_ils/modules/stats/api/report.py
@@ -25,11 +25,13 @@
from rero_ils.modules.libraries.api import LibrariesSearch
from rero_ils.modules.locations.api import LocationsSearch
from rero_ils.modules.patron_types.api import PatronTypesSearch
+from rero_ils.modules.stats_cfg.api import StatConfiguration
from rero_ils.modules.utils import extracted_data_from_ref
from .indicators import NumberOfActivePatronsCfg, NumberOfCirculationCfg, \
NumberOfDeletedItemsCfg, NumberOfDocumentsCfg, NumberOfILLRequests, \
- NumberOfItemsCfg, NumberOfPatronsCfg, NumberOfSerialHoldingsCfg
+ NumberOfItemsCfg, NumberOfPatronsCfg, NumberOfRequestsCfg, \
+ NumberOfSerialHoldingsCfg
from ..api.api import Stat
from ..models import StatType
@@ -42,13 +44,15 @@ def __init__(self, config):
Set variables to create report.
"""
+ if not isinstance(config, StatConfiguration):
+ config = StatConfiguration(data=config)
self.config = config
self.is_active = config.get('is_active', False)
self.indicator = config['category']['indicator']['type']
self.period = config['category']['indicator'].get('period')
self.distributions = config[
'category']['indicator'].get('distributions', [])
- self.org_pid = extracted_data_from_ref(config['organisation'])
+ self.org_pid = config.organisation_pid
self.filter_by_libraries = []
for library in config.get('filter_by_libraries', []):
self.filter_by_libraries.append(extracted_data_from_ref(library))
@@ -88,8 +92,8 @@ def indicator_cfg(self):
'number_of_checkins': NumberOfCirculationCfg(self, 'checkin'),
'number_of_checkouts': NumberOfCirculationCfg(self, 'checkout'),
'number_of_extends': NumberOfCirculationCfg(self, 'extend'),
- 'number_of_requests': NumberOfCirculationCfg(self, 'request'),
- 'number_of_validate_requests': NumberOfCirculationCfg(
+ 'number_of_requests': NumberOfRequestsCfg(self, 'request'),
+ 'number_of_validate_requests': NumberOfRequestsCfg(
self, 'validate_request'),
'number_of_patrons': NumberOfPatronsCfg(self),
'number_of_active_patrons': NumberOfActivePatronsCfg(self)
diff --git a/rero_ils/modules/stats/extensions.py b/rero_ils/modules/stats/extensions.py
index 2073933b60..b8237e406b 100644
--- a/rero_ils/modules/stats/extensions.py
+++ b/rero_ils/modules/stats/extensions.py
@@ -20,7 +20,9 @@
from invenio_records.extensions import RecordExtension
+from rero_ils.modules.libraries.api import Library
from rero_ils.modules.patrons.api import current_librarian
+from rero_ils.modules.utils import extracted_data_from_ref
from .models import StatType
@@ -41,8 +43,14 @@ def pre_dump(self, record, data, dumper=None):
:param dumper: the dumper class used to dump the record.
"""
# to filter the search list results
- if org := record.get('config', {}).get('organisation'):
- record['organisation'] = org
+ if lib := record.get('config', {}).get('library'):
+ lib_pid = (
+ lib.get('pid')
+ or extracted_data_from_ref(lib.get('$ref')))
+ org_pid = Library.get_record_by_pid(lib_pid).organisation_pid
+ record['organisation'] = {
+ 'pid': org_pid
+ }
if not current_librarian:
return
diff --git a/rero_ils/modules/stats/mappings/v7/stats/stat-v0.0.1.json b/rero_ils/modules/stats/mappings/v7/stats/stat-v0.0.1.json
index 703d3298d8..d0ba4fca63 100644
--- a/rero_ils/modules/stats/mappings/v7/stats/stat-v0.0.1.json
+++ b/rero_ils/modules/stats/mappings/v7/stats/stat-v0.0.1.json
@@ -15,9 +15,6 @@
"organisation": {
"type": "object",
"properties": {
- "type": {
- "type": "keyword"
- },
"pid": {
"type": "keyword"
}
@@ -49,7 +46,7 @@
"is_active": {
"type": "boolean"
},
- "organisation": {
+ "library": {
"type": "object",
"properties": {
"type": {
diff --git a/rero_ils/modules/stats_cfg/api.py b/rero_ils/modules/stats_cfg/api.py
index 843d75d8e1..869fd51d97 100644
--- a/rero_ils/modules/stats_cfg/api.py
+++ b/rero_ils/modules/stats_cfg/api.py
@@ -22,7 +22,9 @@
from rero_ils.modules.fetchers import id_fetcher
from rero_ils.modules.minters import id_minter
from rero_ils.modules.providers import Provider
+from rero_ils.modules.utils import extracted_data_from_ref
+from .dumpers import indexer_dumper
from .models import StatCfgIdentifier, StatCfgMetadata
# provider
@@ -58,6 +60,7 @@ class StatConfiguration(IlsRecord):
fetcher = stat_cfg_id_fetcher
provider = StatCfgProvider
model_cls = StatCfgMetadata
+ enable_jsonref = False
def get_links_to_me(self, get_pids=False):
"""Record links.
@@ -92,16 +95,28 @@ def reasons_not_to_delete(self):
"""
cannot_delete = {}
# It is not possible to delete configuration if there are reports.
- links = self.get_links_to_me(self.pid)
+ links = self.get_links_to_me()
if links:
cannot_delete['links'] = links
return cannot_delete
+ @property
+ def organisation_pid(self):
+ """Shortcut for organisation pid."""
+ library = extracted_data_from_ref(self.get('library'), data='record')
+ return library.organisation_pid
+
+ @property
+ def library_pid(self):
+ """Shortcut for library pid."""
+ return extracted_data_from_ref(self.get('library'))
+
class StatsConfigurationIndexer(IlsRecordsIndexer):
"""Indexing stats configuration in Elasticsearch."""
record_cls = StatConfiguration
+ record_dumper = indexer_dumper
def bulk_index(self, record_id_iterator):
"""Bulk index records.
diff --git a/rero_ils/modules/stats_cfg/dumpers.py b/rero_ils/modules/stats_cfg/dumpers.py
new file mode 100644
index 0000000000..b0fe883085
--- /dev/null
+++ b/rero_ils/modules/stats_cfg/dumpers.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+#
+# RERO ILS
+# Copyright (C) 2019-2022 RERO
+# Copyright (C) 2019-2022 UCLouvain
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""Indexing dumper."""
+
+from invenio_records.dumpers import Dumper
+
+from rero_ils.modules.commons.dumpers import MultiDumper, ReplaceRefsDumper
+
+
+class IndexerDumper(Dumper):
+ """Stat configuration dumper."""
+
+ def dump(self, record, data):
+ """Dump a stat configuration.
+
+ Adds the organisation information.
+
+ :param record: The record to dump.
+ :param data: The initial dump data passed in by ``record.dumps()``.
+ """
+ data['organisation'] = dict(pid=record.organisation_pid)
+ return data
+
+
+# dumper used for indexing
+indexer_dumper = MultiDumper(dumpers=[
+ # make a fresh copy
+ Dumper(),
+ ReplaceRefsDumper(),
+ IndexerDumper()
+])
diff --git a/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json b/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json
index 91c39a2120..db3cfc0257 100644
--- a/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json
+++ b/rero_ils/modules/stats_cfg/jsonschemas/stats_cfg/stat_cfg-v0.0.1.json
@@ -17,7 +17,7 @@
"name",
"frequency",
"is_active",
- "organisation",
+ "library",
"category"
],
"properties": {
@@ -68,11 +68,11 @@
"options": [
{
"value": "month",
- "label": "month"
+ "label": "monthly"
},
{
"value": "year",
- "label": "year"
+ "label": "yearly"
}
]
}
@@ -80,13 +80,13 @@
},
"is_active": {
"title": "Active",
- "description": "Is the configuration active.",
+ "description": "Is the configuration active?",
"type": "boolean",
"default": true
},
"filter_by_libraries": {
"title": "Filter numbers by libraries",
- "description": "If enabled, calculate stats only for the resources belonging to the selected libraries. If disabled, stats are calculated for the whole organisation.",
+ "description": "If disabled, stats are calculated for the whole organisation. If enabled, calculate stats only for the resources belonging to the selected libraries.",
"type": "array",
"uniqueItems": true,
"minItems": 0,
@@ -108,9 +108,8 @@
}
}
},
- "organisation": {
- "title": "Organisation",
- "description": "The system librarian's organisation.",
+ "library": {
+ "title": "Library",
"type": "object",
"additionalProperties": false,
"required": [
@@ -118,9 +117,9 @@
],
"properties": {
"$ref": {
- "title": "Organisation URI",
+ "title": "Library URI",
"type": "string",
- "pattern": "^https://bib.rero.ch/api/organisations/.*?$"
+ "pattern": "^https://bib.rero.ch/api/libraries/.*?$"
}
}
},
@@ -195,7 +194,7 @@
"enum": [
"created_month",
"created_year",
- "library",
+ "owning_library",
"imported"
]
},
@@ -215,8 +214,8 @@
"label": "created_year"
},
{
- "value": "library",
- "label": "library"
+ "value": "owning_library",
+ "label": "owning_library"
},
{
"value": "imported",
@@ -267,7 +266,7 @@
"enum": [
"created_month",
"created_year",
- "library"
+ "owning_library"
]
},
"widget": {
@@ -286,8 +285,8 @@
"label": "created_year"
},
{
- "value": "library",
- "label": "library"
+ "value": "owning_library",
+ "label": "owning_library"
}
]
}
@@ -334,8 +333,8 @@
"enum": [
"created_month",
"created_year",
- "library",
- "location",
+ "owning_library",
+ "owning_location",
"type"
]
},
@@ -355,16 +354,16 @@
"label": "created_year"
},
{
- "value": "library",
- "label": "library"
+ "value": "owning_library",
+ "label": "owning_library"
},
{
- "value": "location",
- "label": "location"
+ "value": "owning_location",
+ "label": "owning_location"
},
{
"value": "type",
- "label": "type"
+ "label": "type (standard/issue)"
}
]
}
@@ -378,8 +377,8 @@
"additionalProperties": false,
"propertiesOrder": [
"type",
- "period",
- "distributions"
+ "distributions",
+ "period"
],
"required": [
"type"
@@ -412,7 +411,9 @@
"enum": [
"action_month",
"action_year",
- "library"
+ "owning_library",
+ "item_location",
+ "type"
]
},
"widget": {
@@ -431,16 +432,16 @@
"label": "action_year"
},
{
- "value": "library",
- "label": "library"
+ "value": "owning_library",
+ "label": "owning_library"
},
{
"value": "item_location",
- "label": "item_location"
+ "label": "owning_location"
},
{
"value": "type",
- "label": "type"
+ "label": "type (standard/issue)"
}
]
}
@@ -490,8 +491,7 @@
"additionalProperties": false,
"propertiesOrder": [
"type",
- "distributions",
- "period"
+ "distributions"
],
"required": [
"type"
@@ -548,16 +548,13 @@
},
{
"value": "status",
- "label": "status"
+ "label": "request_status"
}
]
}
}
}
]
- },
- "period": {
- "$ref": "#/definitions/period"
}
}
},
@@ -831,6 +828,7 @@
"definitions": {
"distributions": {
"title": "Distributions",
+ "description": "Up to 2 filters by which to distribute the data in the generated tables. The first value is the lines; the second value is the columns.",
"type": "array",
"minItems": 0,
"default": [],
@@ -839,7 +837,7 @@
},
"period": {
"title": "Period",
- "description": "Time range of data used to calculate the statistics report.",
+ "description": "The time range to filter the data relevant to a specific period: latest month or latest year. Leave empty to not filter the data. Example: process the number of checkouts performed in the last month.",
"type": "string",
"enum": [
"month",
@@ -873,6 +871,7 @@
"transaction_month",
"transaction_year",
"owning_library",
+ "owning_location",
"transaction_location",
"patron_type",
"patron_age",
@@ -893,13 +892,17 @@
"label": "transaction_month"
},
{
- "value": "transaction_yaer",
- "label": "transaction_yaer"
+ "value": "transaction_year",
+ "label": "transaction_year"
},
{
"value": "owning_library",
"label": "owning_library"
},
+ {
+ "value": "owning_location",
+ "label": "owning_location"
+ },
{
"value": "transaction_location",
"label": "transaction_location"
@@ -958,7 +961,7 @@
"options": [
{
"value": "created_month",
- "label": "create_month"
+ "label": "created_month"
},
{
"value": "created_year",
@@ -970,7 +973,7 @@
},
{
"value": "type",
- "label": "type"
+ "label": "patron_type"
},
{
"value": "postal_code",
diff --git a/rero_ils/modules/stats_cfg/mappings/v7/stats_cfg/stat_cfg-v0.0.1.json b/rero_ils/modules/stats_cfg/mappings/v7/stats_cfg/stat_cfg-v0.0.1.json
index 451af492d8..1c2a0c458e 100644
--- a/rero_ils/modules/stats_cfg/mappings/v7/stats_cfg/stat_cfg-v0.0.1.json
+++ b/rero_ils/modules/stats_cfg/mappings/v7/stats_cfg/stat_cfg-v0.0.1.json
@@ -27,6 +27,14 @@
"type": "boolean"
},
"organisation": {
+ "type": "object",
+ "properties": {
+ "pid": {
+ "type": "keyword"
+ }
+ }
+ },
+ "library": {
"type": "object",
"properties": {
"type": {
diff --git a/rero_ils/modules/stats_cfg/permissions.py b/rero_ils/modules/stats_cfg/permissions.py
index 0cbcc6aa10..523fbd08c0 100644
--- a/rero_ils/modules/stats_cfg/permissions.py
+++ b/rero_ils/modules/stats_cfg/permissions.py
@@ -20,6 +20,7 @@
from invenio_access import action_factory
from rero_ils.modules.permissions import AllowedByAction, \
+ AllowedByActionRestrictByManageableLibrary, \
AllowedByActionRestrictByOrganisation, RecordPermissionPolicy
# Actions to control statistics configuration policies for CRUD operations
@@ -37,5 +38,5 @@ class StatisticsConfigurationPermissionPolicy(RecordPermissionPolicy):
can_search = [AllowedByAction(search_action)]
can_read = [AllowedByActionRestrictByOrganisation(read_action)]
can_create = [AllowedByActionRestrictByOrganisation(create_action)]
- can_update = [AllowedByActionRestrictByOrganisation(update_action)]
- can_delete = [AllowedByActionRestrictByOrganisation(delete_action)]
+ can_update = [AllowedByActionRestrictByManageableLibrary(update_action)]
+ can_delete = [AllowedByActionRestrictByManageableLibrary(delete_action)]
diff --git a/rero_ils/modules/stats_cfg/serializers/__init__.py b/rero_ils/modules/stats_cfg/serializers/__init__.py
new file mode 100644
index 0000000000..72f6791bca
--- /dev/null
+++ b/rero_ils/modules/stats_cfg/serializers/__init__.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+#
+# RERO ILS
+# Copyright (C) 2019-2022 RERO
+# Copyright (C) 2019-2022 UCLouvain
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""Stat configuration serialization."""
+
+
+from rero_ils.modules.serializers import RecordSchemaJSONV1, search_responsify
+
+from .json import StatsCfgJSONSerializer
+
+__all__ = [
+ 'json_search'
+]
+
+"""JSON serializer."""
+_json = StatsCfgJSONSerializer(RecordSchemaJSONV1)
+json_search = search_responsify(_json, 'application/rero+json')
diff --git a/rero_ils/modules/stats_cfg/serializers/json.py b/rero_ils/modules/stats_cfg/serializers/json.py
new file mode 100644
index 0000000000..4ae4221cf8
--- /dev/null
+++ b/rero_ils/modules/stats_cfg/serializers/json.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+#
+# RERO ILS
+# Copyright (C) 2019-2023 RERO
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""Statistics configuration serialization."""
+
+from rero_ils.modules.libraries.api import LibrariesSearch
+from rero_ils.modules.serializers import JSONSerializer
+from rero_ils.modules.serializers.mixins import PostprocessorMixin
+
+
+class StatsCfgJSONSerializer(JSONSerializer, PostprocessorMixin):
+ """Mixin serializing records as JSON."""
+
+ def _postprocess_search_aggregations(self, aggregations: dict) -> None:
+ """Post-process aggregations from a search result.
+
+ :param aggregations: the dictionary representing ElasticSearch
+ aggregations section.
+ """
+ JSONSerializer.enrich_bucket_with_data(
+ aggregations.get('library', {}).get('buckets', []),
+ LibrariesSearch, 'name'
+ )
+
+ super()._postprocess_search_aggregations(aggregations)
diff --git a/scripts/setup b/scripts/setup
index ac9ad8d0dc..77d6db7c6b 100755
--- a/scripts/setup
+++ b/scripts/setup
@@ -649,6 +649,7 @@ fi
info_msg "Collect statistics"
eval ${PREFIX} invenio reroils stats collect billing
+eval ${PREFIX} invenio reroils stats collect librarian
eval ${PREFIX} invenio reroils stats report collect-all month
eval ${PREFIX} invenio reroils stats report collect-all year
diff --git a/tests/api/stats/conftest.py b/tests/api/stats/conftest.py
index 786252a2ff..12e17bf7b5 100644
--- a/tests/api/stats/conftest.py
+++ b/tests/api/stats/conftest.py
@@ -23,6 +23,7 @@
from rero_ils.modules.stats.api.api import Stat
from rero_ils.modules.stats.api.librarian import StatsForLibrarian
from rero_ils.modules.stats.api.pricing import StatsForPricing
+from rero_ils.modules.stats.api.report import StatsReport
@pytest.fixture(scope='module')
@@ -58,3 +59,11 @@ def stats_librarian(item_lib_martigny, item_lib_fully, item_lib_sion):
dbcommit=True,
reindex=True
)
+
+
+@pytest.fixture(scope='module')
+def stats_report_martigny(stats_cfg_martigny):
+ """Stats fixture for librarian."""
+ stat_report = StatsReport(stats_cfg_martigny)
+ values = stat_report.collect()
+ yield stat_report.create_stat(values)
diff --git a/tests/api/stats/test_stats_rest.py b/tests/api/stats/test_stats_rest.py
index 9aae03f245..94a75b5629 100644
--- a/tests/api/stats/test_stats_rest.py
+++ b/tests/api/stats/test_stats_rest.py
@@ -110,3 +110,33 @@ def test_stats_librarian_data(
assert not filtered_stat_libs.difference(manageable_libs)
from invenio_db import db
db.session.rollback()
+
+
+@mock.patch('invenio_records_rest.views.verify_record_permission',
+ mock.MagicMock(return_value=VerifyRecordPermissionPatch))
+def test_stats_report_get(client, stats_report_martigny, csv_header):
+ """Test record retrieval."""
+ item_url = url_for(
+ 'invenio_records_rest.stat_item', pid_value=stats_report_martigny.pid)
+ res = client.get(item_url)
+ assert res.status_code == 200
+ assert res.headers['ETag']
+ data = get_json(res)
+ for k in ['created', 'updated', 'metadata', 'links']:
+ assert k in data
+ # Check self links
+ res = client.get(to_relative_url(data['links']['self']))
+ assert res.status_code == 200
+
+ # CSV format
+ params = {'pid_value': stats_report_martigny.pid, 'format': 'csv'}
+ item_url = url_for('invenio_records_rest.stat_item', **params)
+ res = client.get(item_url, headers=csv_header)
+ assert res.status_code == 200
+ data = get_csv(res)
+ assert data
+ list_url = url_for('invenio_records_rest.stat_list')
+ res = client.get(list_url)
+ assert res.status_code == 200
+ data = get_json(res)
+ assert data['hits']['hits']
diff --git a/tests/api/stats_cfg/test_stats_cfg_permissions.py b/tests/api/stats_cfg/test_stats_cfg_permissions.py
index 3fa18937a1..bb9b2f68d1 100644
--- a/tests/api/stats_cfg/test_stats_cfg_permissions.py
+++ b/tests/api/stats_cfg/test_stats_cfg_permissions.py
@@ -27,7 +27,7 @@
def test_stats_cfg_permissions(
patron_martigny, stats_cfg_martigny, stats_cfg_sion,
- librarian_martigny, system_librarian_martigny
+ librarian_martigny, system_librarian_martigny, librarian_saxon, lib_saxon
):
"""Test statistics configuration permissions class."""
@@ -71,6 +71,32 @@ def test_stats_cfg_permissions(
'delete': False
}, stats_cfg_martigny)
+ # Librarian with the right role
+ # cannot update or delete a config of an other lib
+ login_user(librarian_saxon.user)
+ check_permission(StatisticsConfigurationPermissionPolicy, {
+ 'search': True,
+ 'read': True,
+ 'create': True,
+ 'update': False,
+ 'delete': False
+ }, stats_cfg_martigny)
+
+ # Librarian with the right role
+ # can update or delete a config of this library
+ stats_cfg_martigny.update(
+ dict(
+ library={
+ '$ref': f'https://bib.test.rero.ch/libraries/{lib_saxon.pid}'
+ }))
+ check_permission(StatisticsConfigurationPermissionPolicy, {
+ 'search': True,
+ 'read': True,
+ 'create': True,
+ 'update': True,
+ 'delete': True
+ }, stats_cfg_martigny)
+
# System librarian with specific role
# - search/read: any items
login_user(system_librarian_martigny.user)
diff --git a/tests/data/data.json b/tests/data/data.json
index 94d02530b7..9e5a71d0a5 100644
--- a/tests/data/data.json
+++ b/tests/data/data.json
@@ -4381,7 +4381,8 @@
"pro_read_only",
"pro_catalog_manager",
"pro_circulation_manager",
- "pro_user_manager"
+ "pro_user_manager",
+ "pro_statistic_manager"
],
"password": "Pw123456"
},
@@ -5196,8 +5197,8 @@
"pid": "stats_cfg1",
"name": "Statistics configuration for organisation 1",
"description": "test",
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"frequency": "month",
"is_active": true,
@@ -5207,7 +5208,7 @@
"type": "number_of_documents",
"distributions": [
"created_month",
- "library"
+ "owning_library"
]
}
}
@@ -5217,8 +5218,8 @@
"pid": "stats_cfg2",
"name": "Statistics configuration for organisation 2",
"description": "test",
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org2"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib4"
},
"frequency": "month",
"is_active": true,
@@ -5228,7 +5229,7 @@
"type": "number_of_documents",
"distributions": [
"created_month",
- "library"
+ "owning_library"
]
}
}
diff --git a/tests/ui/stats/test_stats_report.py b/tests/ui/stats/test_stats_report.py
index e01fe26022..b2a8f2964a 100644
--- a/tests/ui/stats/test_stats_report.py
+++ b/tests/ui/stats/test_stats_report.py
@@ -26,13 +26,13 @@
from rero_ils.modules.stats.api.report import StatsReport
-def test_stats_report_create(org_martigny, document):
+def test_stats_report_create(lib_martigny, document):
"""Test the stat report creation."""
cfg = {
"$schema":
"https://bib.rero.ch/schemas/stats_cfg/stat_cfg-v0.0.1.json",
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": f"https://bib.rero.ch/api/libraries/{lib_martigny.pid}"
},
"is_active": True,
"pid": "1",
@@ -42,7 +42,7 @@ def test_stats_report_create(org_martigny, document):
"type": "catalogue",
"indicator": {
"type": "number_of_documents",
- "distributions": ['library']
+ "distributions": ['owning_library']
}
}
}
@@ -58,16 +58,16 @@ def test_stats_report_create(org_martigny, document):
))
-def test_stats_report_range(app):
+def test_stats_report_range(app, lib_martigny):
"""Test the report range period."""
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": f"https://bib.rero.ch/api/libraries/{lib_martigny.pid}"
},
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["library"]
+ "distributions": ["owning_library"]
}
}
}
diff --git a/tests/ui/stats/test_stats_report_n_deleted_items.py b/tests/ui/stats/test_stats_report_n_deleted_items.py
index 2104b89a6d..e7af6d0432 100644
--- a/tests/ui/stats/test_stats_report_n_deleted_items.py
+++ b/tests/ui/stats/test_stats_report_n_deleted_items.py
@@ -78,8 +78,8 @@ def test_stats_report_number_of_deleted_items(
}, refresh=True)
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -93,8 +93,8 @@ def test_stats_report_number_of_deleted_items(
# no distributions with filters
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
@@ -110,14 +110,14 @@ def test_stats_report_number_of_deleted_items(
# one distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_deleted_items",
- "distributions": ["library"]
+ "distributions": ["owning_library"]
}
}
}
@@ -128,14 +128,14 @@ def test_stats_report_number_of_deleted_items(
# two distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_deleted_items",
- "distributions": ["library", "action_month"]
+ "distributions": ["owning_library", "action_month"]
}
}
}
@@ -147,14 +147,14 @@ def test_stats_report_number_of_deleted_items(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_deleted_items",
- "distributions": ["action_month", "library"]
+ "distributions": ["action_month", "owning_library"]
}
}
}
@@ -170,14 +170,14 @@ def test_stats_report_number_of_deleted_items(
# year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_deleted_items",
- "distributions": ["action_year", "library"]
+ "distributions": ["action_year", "owning_library"]
}
}
}
@@ -193,15 +193,15 @@ def test_stats_report_number_of_deleted_items(
# limit by period
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_deleted_items",
"period": "year",
- "distributions": ["library"]
+ "distributions": ["owning_library"]
}
}
}
diff --git a/tests/ui/stats/test_stats_report_n_docs.py b/tests/ui/stats/test_stats_report_n_docs.py
index 3d185c7215..3a6365de96 100644
--- a/tests/ui/stats/test_stats_report_n_docs.py
+++ b/tests/ui/stats/test_stats_report_n_docs.py
@@ -68,8 +68,8 @@ def test_stats_report_number_of_documents(
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -83,8 +83,8 @@ def test_stats_report_number_of_documents(
# no distributions with filters
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
@@ -100,14 +100,14 @@ def test_stats_report_number_of_documents(
# one distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["library"]
+ "distributions": ["owning_library"]
}
}
}
@@ -118,14 +118,14 @@ def test_stats_report_number_of_documents(
# two distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["library", "created_month"]
+ "distributions": ["owning_library", "created_month"]
}
}
}
@@ -137,14 +137,14 @@ def test_stats_report_number_of_documents(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["created_month", "library"]
+ "distributions": ["created_month", "owning_library"]
}
}
}
@@ -160,14 +160,14 @@ def test_stats_report_number_of_documents(
# by year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["created_year", "library"]
+ "distributions": ["created_year", "owning_library"]
}
}
}
@@ -183,14 +183,14 @@ def test_stats_report_number_of_documents(
# imported
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["library", "imported"]
+ "distributions": ["owning_library", "imported"]
}
}
}
@@ -202,14 +202,14 @@ def test_stats_report_number_of_documents(
# reverse imported
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_documents",
- "distributions": ["imported", "library"]
+ "distributions": ["imported", "owning_library"]
}
}
}
diff --git a/tests/ui/stats/test_stats_report_n_ill_requests.py b/tests/ui/stats/test_stats_report_n_ill_requests.py
index 493a2d3d01..06dc1b639e 100644
--- a/tests/ui/stats/test_stats_report_n_ill_requests.py
+++ b/tests/ui/stats/test_stats_report_n_ill_requests.py
@@ -18,9 +18,7 @@
"""Stats Report tests for number of ill requests."""
-from datetime import datetime
-import mock
from invenio_search import current_search_client as es
from rero_ils.modules.stats.api.report import StatsReport
@@ -40,8 +38,8 @@ def test_stats_report_number_of_ill_requests(
f'({loc_public_martigny_bourg.pid})'
# no data
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -85,8 +83,8 @@ def test_stats_report_number_of_ill_requests(
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -100,8 +98,8 @@ def test_stats_report_number_of_ill_requests(
# no distributions with filters
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
@@ -115,28 +113,10 @@ def test_stats_report_number_of_ill_requests(
}
assert StatsReport(cfg).collect() == [[1]]
- cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
- },
- "is_active": True,
- "category": {
- "indicator": {
- "period": "year",
- "type": "number_of_ill_requests"
- }
- }
- }
-
- with mock.patch(
- 'rero_ils.modules.stats.api.report.datetime'
- ) as mock_datetime:
- mock_datetime.now.return_value = datetime(year=2024, month=1, day=1)
- assert StatsReport(cfg).collect() == [[2]]
# one distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -154,8 +134,8 @@ def test_stats_report_number_of_ill_requests(
# two distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -174,8 +154,8 @@ def test_stats_report_number_of_ill_requests(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -198,8 +178,8 @@ def test_stats_report_number_of_ill_requests(
# year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -222,8 +202,8 @@ def test_stats_report_number_of_ill_requests(
# type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
diff --git a/tests/ui/stats/test_stats_report_n_items.py b/tests/ui/stats/test_stats_report_n_items.py
index d932bbf284..0d8e230785 100644
--- a/tests/ui/stats/test_stats_report_n_items.py
+++ b/tests/ui/stats/test_stats_report_n_items.py
@@ -30,8 +30,8 @@ def test_stats_report_number_of_items(
"""Test the number of items."""
# no data
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -48,35 +48,62 @@ def test_stats_report_number_of_items(
'type': 'standard',
'organisation': {'pid': org_martigny.pid},
'library': {'pid': lib_martigny.pid},
- 'location': {'pid': loc_public_martigny.pid}
+ 'location': {'pid': loc_public_martigny.pid},
+ 'document': {
+ 'document_type': [{
+ 'main_type': 'docmaintype_book',
+ 'subtype': 'docsubtype_other_book'
+ }]
+ }
})
es.index(index='items', id='2', body={
'_created': "2023-02-01",
'type': 'issue',
'organisation': {'pid': org_martigny.pid},
'library': {'pid': lib_martigny.pid},
- 'location': {'pid': loc_restricted_martigny.pid}
+ 'location': {'pid': loc_restricted_martigny.pid},
+ 'document': {
+ 'document_type': [{
+ 'main_type': 'docmaintype_book',
+ 'subtype': 'docsubtype_other_book'
+ }]
+ }
+
})
es.index(index='items', id='3', body={
'_created': "2024-01-01",
'type': 'provisional',
'organisation': {'pid': org_martigny.pid},
'library': {'pid': lib_martigny_bourg.pid},
- 'location': {'pid': loc_public_martigny_bourg.pid}
+ 'location': {'pid': loc_public_martigny_bourg.pid},
+ 'document': {
+ 'document_type': [{
+ 'main_type': 'docmaintype_book',
+ 'subtype': 'docsubtype_other_book'
+ }]
+ }
+
})
es.index(index='items', id='4', body={
'_created': "2024-01-01",
'type': 'standard',
'organisation': {'pid': org_sion.pid},
'library': {'pid': lib_sion.pid},
- 'location': {'pid': loc_public_sion.pid}
+ 'location': {'pid': loc_public_sion.pid},
+ 'document': {
+ 'document_type': [{
+ 'main_type': 'docmaintype_book',
+ 'subtype': 'docsubtype_other_book'
+ }]
+ }
+
})
es.indices.refresh(index='items')
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -90,8 +117,8 @@ def test_stats_report_number_of_items(
# no distributions with filters
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
@@ -107,14 +134,14 @@ def test_stats_report_number_of_items(
# one distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_items",
- "distributions": ["library"]
+ "distributions": ["owning_library"]
}
}
}
@@ -125,14 +152,14 @@ def test_stats_report_number_of_items(
# two distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_items",
- "distributions": ["library", "created_month"]
+ "distributions": ["owning_library", "created_month"]
}
}
}
@@ -144,14 +171,14 @@ def test_stats_report_number_of_items(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_items",
- "distributions": ["created_month", "library"]
+ "distributions": ["created_month", "owning_library"]
}
}
}
@@ -167,14 +194,14 @@ def test_stats_report_number_of_items(
# year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_items",
- "distributions": ["created_year", "library"]
+ "distributions": ["created_year", "owning_library"]
}
}
}
@@ -190,14 +217,14 @@ def test_stats_report_number_of_items(
# type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_items",
- "distributions": ["type", "library"]
+ "distributions": ["type", "owning_library"]
}
}
}
@@ -214,14 +241,14 @@ def test_stats_report_number_of_items(
# location/type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_items",
- "distributions": ["type", "location"]
+ "distributions": ["type", "owning_location"]
}
}
}
@@ -243,3 +270,37 @@ def test_stats_report_number_of_items(
['provisional', 1, 0, 0],
['standard', 0, 1, 0]
]
+
+ # doc types
+ cfg = {
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
+ },
+ "is_active": True,
+ "category": {
+ "indicator": {
+ "type": "number_of_items",
+ "distributions": ["document_type"]
+ }
+ }
+ }
+ assert StatsReport(cfg).collect() == [
+ ['docmaintype_book', 3]
+ ]
+
+ # doc subtypes
+ cfg = {
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
+ },
+ "is_active": True,
+ "category": {
+ "indicator": {
+ "type": "number_of_items",
+ "distributions": ["document_subtype"]
+ }
+ }
+ }
+ assert StatsReport(cfg).collect() == [
+ ['docsubtype_other_book', 3]
+ ]
diff --git a/tests/ui/stats/test_stats_report_n_patrons.py b/tests/ui/stats/test_stats_report_n_patrons.py
index 72aa18e1dd..0647fc3eee 100644
--- a/tests/ui/stats/test_stats_report_n_patrons.py
+++ b/tests/ui/stats/test_stats_report_n_patrons.py
@@ -35,8 +35,8 @@ def test_stats_report_number_of_patrons(
"""Test the number of patrons and active patrons."""
# no data
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -93,8 +93,8 @@ def test_stats_report_number_of_patrons(
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -107,8 +107,8 @@ def test_stats_report_number_of_patrons(
# gender
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -124,8 +124,8 @@ def test_stats_report_number_of_patrons(
]
# birth year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -141,8 +141,8 @@ def test_stats_report_number_of_patrons(
]
# patron type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -162,8 +162,8 @@ def test_stats_report_number_of_patrons(
]
# postal code
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -179,8 +179,8 @@ def test_stats_report_number_of_patrons(
]
# role
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -196,8 +196,8 @@ def test_stats_report_number_of_patrons(
]
# gender month
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -215,8 +215,8 @@ def test_stats_report_number_of_patrons(
# gender year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -277,8 +277,8 @@ def test_stats_report_number_of_patrons(
# active patrons
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -298,8 +298,8 @@ def test_stats_report_number_of_patrons(
# active patrons
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
diff --git a/tests/ui/stats/test_stats_report_n_serial_holdings.py b/tests/ui/stats/test_stats_report_n_serial_holdings.py
index f2f8e57e6f..d233ac751d 100644
--- a/tests/ui/stats/test_stats_report_n_serial_holdings.py
+++ b/tests/ui/stats/test_stats_report_n_serial_holdings.py
@@ -29,8 +29,8 @@ def test_stats_report_number_of_serial_holdings(
"""Test the number of serials."""
# no data
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -70,8 +70,8 @@ def test_stats_report_number_of_serial_holdings(
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -85,8 +85,8 @@ def test_stats_report_number_of_serial_holdings(
# no distributions with filters
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
@@ -102,14 +102,14 @@ def test_stats_report_number_of_serial_holdings(
# one distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_serial_holdings",
- "distributions": ["library"]
+ "distributions": ["owning_library"]
}
}
}
@@ -120,14 +120,14 @@ def test_stats_report_number_of_serial_holdings(
# two distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_serial_holdings",
- "distributions": ["library", "created_month"]
+ "distributions": ["owning_library", "created_month"]
}
}
}
@@ -139,14 +139,14 @@ def test_stats_report_number_of_serial_holdings(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_serial_holdings",
- "distributions": ["created_month", "library"]
+ "distributions": ["created_month", "owning_library"]
}
}
}
@@ -162,14 +162,14 @@ def test_stats_report_number_of_serial_holdings(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
"indicator": {
"type": "number_of_serial_holdings",
- "distributions": ["created_year", "library"]
+ "distributions": ["created_year", "owning_library"]
}
}
}
diff --git a/tests/ui/stats/test_stats_report_number_of_ciculation.py b/tests/ui/stats/test_stats_report_number_of_ciculation.py
index 9d9aa4ca92..67e5229191 100644
--- a/tests/ui/stats/test_stats_report_number_of_ciculation.py
+++ b/tests/ui/stats/test_stats_report_number_of_ciculation.py
@@ -86,8 +86,8 @@ def test_stats_report_circulation_trigger(
}, refresh=True)
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -99,8 +99,8 @@ def test_stats_report_circulation_trigger(
assert StatsReport(cfg).collect() == [[2]]
lib_pid = lib_martigny_bourg.pid
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"filter_by_libraries": [{
@@ -251,8 +251,8 @@ def test_stats_report_number_of_checkins(
# no distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -265,8 +265,8 @@ def test_stats_report_number_of_checkins(
# limit by period
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -284,8 +284,8 @@ def test_stats_report_number_of_checkins(
# one distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -301,8 +301,8 @@ def test_stats_report_number_of_checkins(
]
# two distributions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -320,8 +320,8 @@ def test_stats_report_number_of_checkins(
# reverse distrubtions
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -343,8 +343,8 @@ def test_stats_report_number_of_checkins(
# year
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -366,8 +366,8 @@ def test_stats_report_number_of_checkins(
# patron type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -384,8 +384,8 @@ def test_stats_report_number_of_checkins(
# patron age
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -402,8 +402,8 @@ def test_stats_report_number_of_checkins(
# postal code
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -420,8 +420,8 @@ def test_stats_report_number_of_checkins(
# patron type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -438,8 +438,8 @@ def test_stats_report_number_of_checkins(
# patron type
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -456,8 +456,8 @@ def test_stats_report_number_of_checkins(
# transaction channel
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -474,8 +474,8 @@ def test_stats_report_number_of_checkins(
# owning library
cfg = {
- "organisation": {
- "$ref": "https://bib.rero.ch/api/organisations/org1"
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
"is_active": True,
"category": {
@@ -489,3 +489,105 @@ def test_stats_report_number_of_checkins(
[f'{lib_martigny_bourg.get("name")} ({lib_martigny_bourg.pid})', 1],
[f'{lib_martigny.get("name")} ({lib_martigny.pid})', 1]
]
+
+ # owning location
+ cfg = {
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
+ },
+ "is_active": True,
+ "category": {
+ "indicator": {
+ "type": "number_of_checkins",
+ "distributions": ["owning_location"]
+ }
+ }
+ }
+ assert StatsReport(cfg).collect() == [
+ [loc_public_martigny_bourg['name'], 1],
+ [loc_public_martigny['name'], 1]
+ ]
+
+
+def test_stats_report_number_of_requests(
+ lib_martigny, lib_martigny_bourg, loc_public_martigny,
+ loc_public_martigny_bourg):
+ """Test the number of circulation checkins operations."""
+ label_loc_pub_martigny = f'{lib_martigny["name"]} / '\
+ f'{loc_public_martigny["name"]} ({loc_public_martigny.pid})'
+ label_loc_pub_martigny_bourg = f'{lib_martigny_bourg["name"]} / '\
+ f'{loc_public_martigny_bourg["name"]} '\
+ f'({loc_public_martigny_bourg.pid})'
+
+ # fixtures
+ es.index(index='operation_logs-2020', id='1', body={
+ "date": "2023-01-01",
+ "loan": {
+ "trigger": "request",
+ "item": {
+ "document": {
+ "type": "docsubtype_other_book"
+ },
+ "library_pid": lib_martigny.pid,
+ "holding": {
+ "location_name": loc_public_martigny["name"]
+ }
+ },
+ "transaction_location": {"pid": loc_public_martigny.pid},
+ "pickup_location": {"pid": loc_public_martigny.pid},
+ "transaction_channel": "sip2",
+ "patron": {
+ "age": 13,
+ "type": "Usager.ère moins de 14 ans",
+ "postal_code": "1920"
+ }
+ },
+ "record": {
+ "type": "loan",
+ }
+ }, refresh=True)
+
+ es.index(index='operation_logs-2020', id='2', body={
+ "date": "2024-01-01",
+ "loan": {
+ "trigger": "request",
+ "item": {
+ "document": {
+ "type": "ebook"
+ },
+ "library_pid": lib_martigny_bourg.pid,
+ "holding": {
+ "location_name": loc_public_martigny_bourg["name"]
+ }
+ },
+ "transaction_location": {"pid": loc_public_martigny_bourg.pid},
+ "pickup_location": {"pid": loc_public_martigny_bourg.pid},
+ "transaction_channel": "system",
+ "patron": {
+ "age": 30,
+ "type": "Usager.ère plus de 18 ans",
+ "postal_code": "1930"
+ }
+ },
+ "record": {
+ "type": "loan",
+ }
+ }, refresh=True)
+
+ # pickup location
+ cfg = {
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
+ },
+ "is_active": True,
+ "category": {
+ "indicator": {
+ "type": "number_of_requests",
+ "distributions": ["pickup_location"]
+ }
+ }
+ }
+ assert StatsReport(cfg).collect() == [
+ [label_loc_pub_martigny_bourg, 1],
+ [label_loc_pub_martigny, 1]
+ ]
diff --git a/tests/ui/stats/test_stats_tasks.py b/tests/ui/stats/test_stats_tasks.py
new file mode 100644
index 0000000000..e4c21f7d6f
--- /dev/null
+++ b/tests/ui/stats/test_stats_tasks.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+#
+# RERO ILS
+# Copyright (C) 2023 RERO
+# Copyright (C) 2023 UCL
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+"""Stats Report tests celery tasks."""
+
+from rero_ils.modules.stats.tasks import collect_stats_reports
+
+
+def test_stats_task_report(stats_cfg_martigny):
+ """Test stat task reports generation."""
+ res = collect_stats_reports('year')
+ assert not res
+
+ res = collect_stats_reports('month')
+ assert len(res) > 0
diff --git a/tests/unit/test_stats_cfg_jsonschema.py b/tests/unit/test_stats_cfg_jsonschema.py
index dabeaba4bb..a65b7454d2 100644
--- a/tests/unit/test_stats_cfg_jsonschema.py
+++ b/tests/unit/test_stats_cfg_jsonschema.py
@@ -45,8 +45,8 @@ def test_valid_circulation_n_docs(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'catalogue',
@@ -56,7 +56,8 @@ def test_valid_circulation_n_docs(stats_cfg_schema):
},
'is_active': True
}
- for dist in ['created_month', 'created_year', 'imported', 'library']:
+ for dist in ['created_month', 'created_year', 'imported',
+ 'owning_library']:
data['category']['indicator']['distributions'] = [dist]
validate(data, stats_cfg_schema)
@@ -74,8 +75,8 @@ def test_valid_circulation_n_serial_holdings(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'catalogue',
@@ -85,7 +86,7 @@ def test_valid_circulation_n_serial_holdings(stats_cfg_schema):
},
'is_active': True
}
- for dist in ['created_month', 'created_year', 'library']:
+ for dist in ['created_month', 'created_year', 'owning_library']:
data['category']['indicator']['distributions'] = [dist]
validate(data, stats_cfg_schema)
@@ -103,8 +104,8 @@ def test_valid_circulation_n_items(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'catalogue',
@@ -114,9 +115,8 @@ def test_valid_circulation_n_items(stats_cfg_schema):
},
'is_active': True
}
- for dist in [
- 'created_month', 'created_year', 'library', 'location', 'type'
- ]:
+ for dist in ['created_month', 'created_year', 'owning_library',
+ 'owning_location', 'type']:
data['category']['indicator']['distributions'] = [dist]
validate(data, stats_cfg_schema)
@@ -134,8 +134,8 @@ def test_valid_circulation_n_patrons(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'user_management',
@@ -166,8 +166,8 @@ def test_valid_circulation_n_active_patrons(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'user_management',
@@ -200,8 +200,8 @@ def test_valid_circulation_n_deleted_items(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'catalogue',
@@ -213,7 +213,7 @@ def test_valid_circulation_n_deleted_items(stats_cfg_schema):
}
for period in ['year', 'month']:
data['category']['indicator']['period'] = period
- for dist in ['action_month', 'action_year', 'library']:
+ for dist in ['action_month', 'action_year', 'owning_library']:
data['category']['indicator']['distributions'] = [dist]
validate(data, stats_cfg_schema)
@@ -234,9 +234,9 @@ def test_valid_circulation_n_ill_requests(stats_cfg_schema):
'pid': 'statcfg1',
'name': 'foo',
'description': 'bar',
- 'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "frequency": "month",
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'circulation',
@@ -246,19 +246,13 @@ def test_valid_circulation_n_ill_requests(stats_cfg_schema):
},
'is_active': True
}
- for period in ['year', 'month']:
- data['category']['indicator']['period'] = period
- for dist in [
- 'created_month', 'created_year', 'pickup_location', 'status'
- ]:
- data['category']['indicator']['distributions'] = [dist]
- validate(data, stats_cfg_schema)
-
- data['category']['indicator']['distributions'] = ["foo"]
- with pytest.raises(ValidationError):
+ for dist in [
+ 'created_month', 'created_year', 'pickup_location', 'status'
+ ]:
+ data['category']['indicator']['distributions'] = [dist]
validate(data, stats_cfg_schema)
- data['category']['indicator']['period'] = 'day'
+ data['category']['indicator']['distributions'] = ["foo"]
with pytest.raises(ValidationError):
validate(data, stats_cfg_schema)
@@ -272,8 +266,8 @@ def test_valid_circulation_n_circulations(stats_cfg_schema):
'name': 'foo',
'description': 'bar',
'frequency': 'month',
- 'organisation': {
- '$ref': 'https://bib.rero.ch/api/organisations/org1'
+ "library": {
+ "$ref": "https://bib.rero.ch/api/libraries/lib1"
},
'category': {
'type': 'circulation',