Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajax recent view #199

Merged
merged 5 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ CKAN version | Compatibility

### Configuration

_TODO: what configuraiton options exist?_
[Optional]
`ckanext.datagovtheme.js_recent_view = true`


This defaults to `false`. If displaying the recent view count slows down page loading, the optional parameter can be set to `true` to make the recent view count an AJAX call, improving page loading speed. If the recent view count information (package['tracking_summary']) is already present, the AJAX call is disabled to reduce overhead. Therefore, the built-in recent view count rendering must be disabled for this mechanism to take effect.
FuhuXia marked this conversation as resolved.
Show resolved Hide resolved


## Development
Expand Down
17 changes: 13 additions & 4 deletions ckanext/datagovtheme/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
from ckan.plugins.toolkit import config
from ckan.lib.base import abort
from ckan.common import c, request
from ckanext.datagovtheme import helpers

from flask import Blueprint, redirect
from flask import Blueprint, redirect, jsonify

import logging
log = logging.getLogger(__name__)

pusher = Blueprint('datagovtheme', __name__)
datagovtheme_bp = Blueprint('datagovtheme', __name__)


def show():
Expand All @@ -35,7 +36,15 @@ def show():

def redirect_homepage():
CKAN_SITE_URL = config.get("ckan.site_url")
return redirect(CKAN_SITE_URL + "/dataset", code=302)
return redirect(CKAN_SITE_URL + "/dataset/", code=302)


pusher.add_url_rule('/', view_func=redirect_homepage)
def get_popuplar_count():
pkgs = request.args.get('pkgs')
return jsonify(helpers.get_pkgs_popular_count(pkgs))


datagovtheme_bp.add_url_rule('/', view_func=redirect_homepage)
datagovtheme_bp.add_url_rule("/datagovtheme/get-popular-count",
methods=['GET'],
view_func=get_popuplar_count)
36 changes: 36 additions & 0 deletions ckanext/datagovtheme/fanstatic_library/scripts/popular.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// This js file loads with datagov-popular.html snippet

jQuery(function ($) {

// This api takes a list of package ids from querystring and returns the view count for each package
var pupolar_api = "/datagovtheme/get-popular-count";
FuhuXia marked this conversation as resolved.
Show resolved Hide resolved

// all ids in a string, comma separated
var pkgs = {'pkgs': collect_all_packages().join(',')};

$.getJSON(pupolar_api, pkgs, function(data) {
$.each( data, function( key, val ) {
$("ul.dataset-list li.dataset-item h3.dataset-heading").each(function() {
if ($(this).attr('pkg_id') == key) {
$(this).find('span.recent-views-datagov').attr('title', val['recent']).html('<i class="fa fa-line-chart"></i> ' + val['recent'] + ' recent views');
// If the view count is greater than 10, make it visible
if (val['recent'] >= 10) {
$(this).find('span.recent-views').css('visibility', 'visible');
}else{
$(this).find('span.recent-views').css('display', 'hidden');
}
}
});
});
});

// We have pkg_id in each dataset-item, added in the template.
function collect_all_packages() {
var pkgs = [];
$("ul.dataset-list li.dataset-item h3.dataset-heading").each(function() {
pkgs.push($(this).attr('pkg_id'));
});

return pkgs;
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -2239,6 +2239,9 @@ body .tagged-ngda {
padding-left: 5px;
display: inline-block;
}
.recent-views-datagov {
visibility: hidden;
}
.bar-image {
width: 18px;
display: inline-block;
Expand Down
5 changes: 5 additions & 0 deletions ckanext/datagovtheme/fanstatic_library/webassets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ location_autocomplete_js:
# TODO can this custom location search be replaced by ckanext-spatial? Can
# this be pushed upstream to ckanext-spatial as an enhancement?
- scripts/location_autocomplete.js

popular-js:
output: datagovtheme/popular.js
contents:
- scripts/popular.js
22 changes: 22 additions & 0 deletions ckanext/datagovtheme/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import pkg_resources

from ckan import plugins as p
from ckan.lib import base
from ckan.lib import helpers as h
from ckan import model
from ckanext.harvest.model import HarvestObject
Expand Down Expand Up @@ -762,3 +763,24 @@ def get_login_url():
return h.url_for(controller='user', action='login')

return '/user/saml2login'


def get_pkgs_popular_count(ids):
populars = {}
if ids:
pkg_ids = ids.split(',')
for pkg_id in pkg_ids:
populars[pkg_id] = model.TrackingSummary.get_for_package(pkg_id)
return populars


def render_popular(type, pkg, min, title=''):
# If the package has tracking_summary, call core helper function to render it.
if pkg.get('tracking_summary'):
return h.popular(type, pkg['tracking_summary']['recent'], min, title)

js_recent_view = asbool(config.get('ckanext.datagovtheme.js_recent_view', False))
if js_recent_view:
return base.render_snippet('snippets/datagov_popular.html')

return ''
4 changes: 3 additions & 1 deletion ckanext/datagovtheme/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ def get_helpers(self):
'convert_top_category_to_list': datagovtheme_helpers.convert_top_category_to_list,
'get_pkg_dict_extra': datagovtheme_helpers.get_pkg_dict_extra,
'get_login_url': datagovtheme_helpers.get_login_url,
'get_pkgs_popular_count': datagovtheme_helpers.get_pkgs_popular_count,
'render_popular': datagovtheme_helpers.render_popular,
}

# https://github.com/GSA/ckan/blob/datagov/ckan/config/environment.py#L70:L70
Expand All @@ -125,4 +127,4 @@ def get_helpers(self):
return helpers

def get_blueprint(self):
return blueprint.pusher
return blueprint.datagovtheme_bp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
{% if res.name == 'Comma Seperated Values File' %}
<a itemprop="contentUrl" class="heading" href="{{ url }}" title="Comma Separated Values File">
Comma Separated Values File<span class="format-label" property="dc:format" data-format="{{ res.format.lower() or 'data' }}">{{ res.format }}</span>
{{ h.popular('views', res.tracking_summary.total, min=10) }}
{{ h.popular('views', res.tracking_summary.total, min=10) if res.tracking_summary }}
</a>
{% else %}
<a itemprop="contentUrl" class="heading" href="{{ url }}" title="{{ res.name or res.description }}">
{{ h.resource_display_name(res) | truncate(50) }}<span class="format-label" property="dc:format" data-format="{{ res.format.lower() or 'data' }}">{{ res.format }}</span>
{{ h.popular('views', res.tracking_summary.total, min=10) }}
{{ h.popular('views', res.tracking_summary.total, min=10) if res.tracking_summary }}
</a>
{% endif %}
{% endblock %}
Expand Down
5 changes: 5 additions & 0 deletions ckanext/datagovtheme/templates/snippets/datagov_popular.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<span class="recent-views recent-views-datagov" title="" xmlns="http://www.w3.org/1999/xhtml">
<i class="fa fa-line-chart"></i> &nbsp;&nbsp; recent views
</span>
{% asset 'datagovtheme/popular-js' %}

4 changes: 2 additions & 2 deletions ckanext/datagovtheme/templates/snippets/package_item.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
</span>
</div>
{% endif %}
<h3 class="dataset-heading">
<h3 class="dataset-heading" pkg_id="{{ package.id }}">
{% if package.private %}
<span class="dataset-private label label-inverse">
<i class="fa fa-lock"></i>
Expand Down Expand Up @@ -63,7 +63,7 @@ <h3 class="dataset-heading">
</span>
{% endif %}

{{ h.popular('recent views', package.tracking_summary.recent, min=10) if package.tracking_summary }}
{{ h.render_popular('recent views', package, min=10) }}
</h3>
<div class="notes">
{% if organization and show_organization %}
Expand Down
63 changes: 63 additions & 0 deletions ckanext/datagovtheme/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# encoding: utf-8
import logging
import re
import datetime

import mock
import pytest

from ckanext.datagovtheme import helpers
import ckan.tests.factories as factories
import ckan.lib.helpers as h


################
Expand Down Expand Up @@ -136,3 +138,64 @@ def test_is_tagged_ngda():

assert helpers.is_tagged_ngda(dataset_ngda) is True
assert helpers.is_tagged_ngda(dataset_non_ngda) is False


##################
# recent view
##################


@pytest.fixture
def track(app):
"""Post some data to /_tracking directly.

This simulates what's supposed when you view a page with tracking
enabled (an ajax request posts to /_tracking).

"""

def func(url, type_="page", ip="199.204.138.90", browser="firefox"):
params = {"url": url, "type": type_}
extra_environ = {
# The tracking middleware crashes if these aren't present.
"HTTP_USER_AGENT": browser,
"REMOTE_ADDR": ip,
"HTTP_ACCEPT_LANGUAGE": "en",
"HTTP_ACCEPT_ENCODING": "gzip, deflate",
}
app.post("/_tracking", params=params, extra_environ=extra_environ)

return func


def update_tracking_summary():
"""Update CKAN's tracking summary data."""

import ckan.cli.tracking as tracking
import ckan.model

date = (datetime.datetime.now() - datetime.timedelta(days=1)).strftime(
"%Y-%m-%d"
)
tracking.update_all(engine=ckan.model.meta.engine, start_date=date)


def test_get_pkgs_popular_count(track):
factories.Dataset(id="view-id-1", name="view-id-1")
factories.Dataset(id="view-id-2", name="view-id-2")

ids = "view-id-1,view-id-2"
assert helpers.get_pkgs_popular_count(ids) == {
'view-id-1': {'recent': 0, 'total': 0},
'view-id-2': {'recent': 0, 'total': 0}
}

url = h.url_for("dataset.read", id="view-id-1")
track(url)
update_tracking_summary()

# after dataset 1 is viewed, it should have a view count of 1, dataset 2 should still have 0
assert helpers.get_pkgs_popular_count(ids) == {
'view-id-1': {'recent': 1, 'total': 1},
'view-id-2': {'recent': 0, 'total': 0}
}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setup(
name="ckanext-datagovtheme",
version="0.2.24",
version="0.2.25",
description="CKAN Extension to manage data.gov theme",
long_description=long_description,
classifiers=[
Expand Down
Loading