diff --git a/ckanext/api_tracking/auth/csv.py b/ckanext/api_tracking/auth/csv.py index ff4ad30..d401942 100644 --- a/ckanext/api_tracking/auth/csv.py +++ b/ckanext/api_tracking/auth/csv.py @@ -2,6 +2,10 @@ def most_accessed_dataset_with_token_csv(context, data_dict): return {'success': False} +def most_accessed_resource_with_token_csv(context, data_dict): + return {'success': False} + + def most_accessed_token_csv(context, data_dict): return {'success': False} diff --git a/ckanext/api_tracking/blueprints/csv.py b/ckanext/api_tracking/blueprints/csv.py index 169ecba..373c176 100644 --- a/ckanext/api_tracking/blueprints/csv.py +++ b/ckanext/api_tracking/blueprints/csv.py @@ -10,6 +10,7 @@ all_token_usage_data, most_accessed_token_data, most_accessed_dataset_with_token_data, + most_accessed_resource_with_token_data, ) @@ -52,6 +53,18 @@ def most_accessed_dataset_with_token_csv(): ) +@tracking_csv_blueprint.route('/most-accessed-resource-with-token.csv', methods=["GET"]) +def most_accessed_resource_with_token_csv(): + """ Get most accessed (using a API token) resources """ + + return _csv_response( + 'most_accessed_resource_with_token_csv', + most_accessed_resource_with_token_data, + {'limit': 10}, + 'most-accessed-dataset-with-token.csv', + ) + + @tracking_csv_blueprint.route('/most-accessed-token.csv', methods=["GET"]) def most_accessed_token_csv(): """ Get most accessed tokens """ diff --git a/ckanext/api_tracking/plugin.py b/ckanext/api_tracking/plugin.py index a703ae4..f72ce0b 100644 --- a/ckanext/api_tracking/plugin.py +++ b/ckanext/api_tracking/plugin.py @@ -50,6 +50,8 @@ def get_auth_functions(self): "all_token_usage_csv": auth_csv.all_token_usage_csv, "most_accessed_dataset_with_token": auth_queries.most_accessed_dataset_with_token, "most_accessed_dataset_with_token_csv": auth_csv.most_accessed_dataset_with_token_csv, + "most_accessed_resource_with_token": auth_queries.most_accessed_resource_with_token, + "most_accessed_resource_with_token_csv": auth_csv.most_accessed_resource_with_token_csv, "most_accessed_token": auth_queries.most_accessed_token, "most_accessed_token_csv": auth_csv.most_accessed_token_csv, } diff --git a/ckanext/api_tracking/tests/factories.py b/ckanext/api_tracking/tests/factories.py index c430ea7..e1e2289 100644 --- a/ckanext/api_tracking/tests/factories.py +++ b/ckanext/api_tracking/tests/factories.py @@ -80,6 +80,12 @@ class TrackingUsageAPIDataset(TrackingUsageF): object_type = 'dataset' +class TrackingUsageAPIResource(TrackingUsageF): + tracking_type = 'api' + tracking_sub_type = 'show' + object_type = 'resource' + + class TrackingUsageAPIResourceDownload(TrackingUsageF): tracking_type = 'api' tracking_sub_type = 'download' diff --git a/ckanext/api_tracking/tests/test_csv.py b/ckanext/api_tracking/tests/test_csv_dataset.py similarity index 100% rename from ckanext/api_tracking/tests/test_csv.py rename to ckanext/api_tracking/tests/test_csv_dataset.py diff --git a/ckanext/api_tracking/tests/test_csv_resource.py b/ckanext/api_tracking/tests/test_csv_resource.py new file mode 100644 index 0000000..c347669 --- /dev/null +++ b/ckanext/api_tracking/tests/test_csv_resource.py @@ -0,0 +1,82 @@ +import pytest +from types import SimpleNamespace +from ckan.plugins import toolkit +from ckan.lib.helpers import url_for +from ckan.tests import factories + +from ckanext.api_tracking.tests import factories as tf + + +@pytest.fixture +def base_data(): + obj = SimpleNamespace() + obj.sysadmin = factories.SysadminWithToken() + obj.user1 = factories.UserWithToken() + obj.user2 = factories.UserWithToken() + obj.dataset1 = factories.Dataset() + obj.dataset2 = factories.Dataset() + obj.resource11 = factories.Resource(package_id=obj.dataset1['id']) + obj.resource12 = factories.Resource(package_id=obj.dataset1['id']) + obj.resource13 = factories.Resource(package_id=obj.dataset1['id']) + obj.resource21 = factories.Resource(package_id=obj.dataset2['id']) + obj.resource22 = factories.Resource(package_id=obj.dataset2['id']) + obj.trackings = [] + for user in [obj.user1, obj.user1, obj.user2]: + for dataset in [obj.dataset1, obj.dataset1, obj.dataset2]: + new_tracking = tf.TrackingUsageAPIDataset(user=user, object_id=dataset['id']) + obj.trackings.append(new_tracking) + for resource in [obj.resource11, obj.resource12, obj.resource13, obj.resource21, obj.resource22]: + new_tracking = tf.TrackingUsageAPIResource(user=user, object_id=resource['id']) + obj.trackings.append(new_tracking) + for resource in [obj.resource11, obj.resource12, obj.resource13, obj.resource21, obj.resource22]: + new_tracking = tf.TrackingUsageAPIResourceDownload(user=user, object_id=resource['id']) + obj.trackings.append(new_tracking) + return obj + + +@pytest.mark.usefixtures('clean_db', 'api_tracking_migrate') +class TestTrackingCSVView: + """ Test basic tracking from requests """ + def test_dataset_with_token_csv_no_user(self, app): + """ Test the endpoint is closed for anonymous users """ + url = url_for('tracking_csv.most_accessed_resource_with_token_csv') + with pytest.raises(toolkit.NotAuthorized): + app.get(url) + + def test_dataset_with_token_csv_no_auth(self, app, base_data): + """ Test the endpoint is closed for regular users """ + url = url_for('tracking_csv.most_accessed_resource_with_token_csv') + auth = {"Authorization": base_data.user1['token']} + with pytest.raises(toolkit.NotAuthorized): + app.get(url, extra_environ=auth) + + def test_dataset_with_token_csv(self, app, base_data): + url = url_for('tracking_csv.most_accessed_resource_with_token_csv') + # download the CSV + auth = {"Authorization": base_data.sysadmin['token']} + response = app.get(url, extra_environ=auth) + assert response.status_code == 200 + # save the response locally + full_response = response.body + with open('most-accessed-dataset-with-token.csv', 'w') as f: + f.write(full_response) + + # check the CSV content + lines = full_response.splitlines() + header = lines[0].split(',') + assert header == ['dataset_id', 'dataset_title', 'dataset_url', 'total'] + rows = lines[1:] + # They are just two datasets + assert len(rows) == 2 + for row in rows: + fields = row.split(',') + if fields[0] == base_data.dataset1['id']: + assert fields[1] == base_data.dataset1['title'] + assert fields[2] == url_for('dataset.read', id=base_data.dataset1['name'], qualified=True) + assert fields[3] == '6' + elif fields[0] == base_data.dataset2['id']: + assert fields[1] == base_data.dataset2['title'] + assert fields[2] == url_for('dataset.read', id=base_data.dataset2['name'], qualified=True) + assert fields[3] == '3' + else: + assert False, f"Unexpected dataset id: {fields[0]}"