This CKAN Extension customises a CKAN instance for the hosting of data.gov.au. | This CKAN Extension customises a CKAN instance for the hosting of data.gov.au. |
It comprises: | It comprises: |
* A CKAN Extension "plugin" at ``ckanext/datagovau/plugin.py`` which, when | * A CKAN Extension "plugin" at ``ckanext/datagovau/plugin.py`` which, when |
loaded, overrides various settings in the core ``ini``-file to provide: | loaded, overrides various settings in the core ``ini``-file to provide: |
* A path to local customisations of the core templates | * A path to local customisations of the core templates to include AGLS/Dublin Core minimum metadata |
* A custom Package edit form that defaults to cc-by licence | * A custom Package edit form that defaults to cc-by licence |
* A custom n3/rdf output format | * A custom n3/rdf output format |
* Replaces links with http/https protocol independent versions | |
* Provides HTML to users to embed data previews on their own website | |
* A cut down licenses.json file | * A cut down licenses.json file |
Installation | Installation |
============ | ============ |
To install this package, from your CKAN virtualenv, run the following from your CKAN base folder (e.g. ``pyenv/``):: | To install this package, from your CKAN virtualenv, run the following from your CKAN base folder (e.g. ``pyenv/``):: |
pip install -e git+https://github.com/okfn/ckanext-datagovau#egg=ckanext-datagovau | pip install -e git+https://github.com/okfn/ckanext-datagovau#egg=ckanext-datagovau |
Then activate it by setting ``ckan.plugins = datagovau`` in your main ``ini``-file. | Then activate it by setting ``ckan.plugins = datagovau`` in your main ``ini``-file. |
To add the cut down licenses.json set ``licenses_group_url = http://%(ckan.site_url)/licenses.json`` | To add the cut down licenses.json set ``licenses_group_url = http://%(ckan.site_url)/licenses.json`` |
or copy ``ckanext/datagovau/theme/public/licenses.json`` to the same folder as your CKAN config ini file | or copy ``ckanext/datagovau/theme/public/licenses.json`` to the same folder as your CKAN config ini file |
and set ``licenses_group_url = file://%(here)s/licenses.json`` | and set ``licenses_group_url = file://%(here)s/licenses.json`` |
import urllib | import urllib |
import json | import json |
from pprint import pprint | from pprint import pprint |
import logging | import logging |
import ckan.logic as logic | import ckan.logic as logic |
import hashlib | import hashlib |
import threading | import threading |
from ckan.common import _, c, request, response | from ckan.common import _, c, request, response |
from pylons import config | from pylons import config |
from webob.multidict import UnicodeMultiDict | |
from paste.util.multidict import MultiDict | |
log = logging.getLogger(__name__) | log = logging.getLogger(__name__) |
from ckan.controllers.api import ApiController | from ckan.controllers.api import ApiController |
class DGAApiController(ApiController): | class DGAApiController(ApiController): |
def _post_analytics(self,user,request_obj_type,request_function,request_id): | def _post_analytics(self,user,request_obj_type,request_function,request_id): |
if (config.get('googleanalytics.id') != None): | if (config.get('googleanalytics.id') != None): |
data = urllib.urlencode({ | data = urllib.urlencode({ |
"v":1, | "v":1, |
"tid":config.get('googleanalytics.id'), | "tid":config.get('googleanalytics.id'), |
"cid":hashlib.md5(user).hexdigest(), | "cid":hashlib.md5(user).hexdigest(), |
"t":"event", | "t":"event", |
"dh":c.environ['HTTP_HOST'], | "dh":c.environ['HTTP_HOST'], |
"dp":c.environ['PATH_INFO'], | "dp":c.environ['PATH_INFO'], |
"dr":c.environ.get('HTTP_REFERER',''), | "dr":c.environ.get('HTTP_REFERER',''), |
"ec":"CKAN API Request", | "ec":"CKAN API Request", |
"ea":request_obj_type+request_function, | "ea":request_obj_type+request_function, |
"el":request_id, | "el":request_id, |
}) | }) |
log.debug("Sending API Analytics Data: "+data) | log.debug("Sending API Analytics Data: "+data) |
# send analytics asynchronously | # send analytics asynchronously |
threading.Thread(target=urllib.urlopen,args=("http://www.google-analytics.com/collect", data)).start() | threading.Thread(target=urllib.urlopen,args=("http://www.google-analytics.com/collect", data)).start() |
def action(self, logic_function, ver=None): | def action(self, logic_function, ver=None): |
try: | try: |
function = logic.get_action(logic_function) | function = logic.get_action(logic_function) |
except Exception,e: | except Exception,e: |
log.debug(e) | log.debug(e) |
pass | pass |
try: | try: |
side_effect_free = getattr(function, 'side_effect_free', False) | side_effect_free = getattr(function, 'side_effect_free', False) |
request_data = self._get_request_data(try_url_params=side_effect_free) | request_data = self._get_request_data(try_url_params=side_effect_free) |
if isinstance(request_data, dict): | if isinstance(request_data, dict): |
id = request_data.get('id','') | id = request_data.get('id','') |
if 'q' in request_data.keys(): | if 'q' in request_data.keys(): |
id = request_data['q'] | id = request_data['q'] |
if 'query' in request_data.keys(): | if 'query' in request_data.keys(): |
id = request_data['query'] | id = request_data['query'] |
self._post_analytics(c.user,logic_function,'', id) | self._post_analytics(c.user,logic_function,'', id) |
except Exception,e: | except Exception,e: |
print log.debug(e) | print log.debug(e) |
pass | pass |
return ApiController.action(self,logic_function, ver) | return ApiController.action(self,logic_function, ver) |
def list(self, ver=None, register=None, subregister=None, id=None): | def list(self, ver=None, register=None, subregister=None, id=None): |
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"list",id) | self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"list",id) |
return ApiController.list(self,ver, register, subregister, id) | return ApiController.list(self,ver, register, subregister, id) |
def show(self, ver=None, register=None, subregister=None, id=None, id2=None): | def show(self, ver=None, register=None, subregister=None, id=None, id2=None): |
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"show",id) | self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"show",id) |
return ApiController.show(self,ver, register, subregister, id,id2) | return ApiController.show(self,ver, register, subregister, id,id2) |
def update(self, ver=None, register=None, subregister=None, id=None, id2=None): | def update(self, ver=None, register=None, subregister=None, id=None, id2=None): |
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"update",id) | self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"update",id) |
return ApiController.update(self,ver, register, subregister, id,id2) | return ApiController.update(self,ver, register, subregister, id,id2) |
def delete(self, ver=None, register=None, subregister=None, id=None, id2=None): | def delete(self, ver=None, register=None, subregister=None, id=None, id2=None): |
self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"delete",id) | self._post_analytics(c.user,register+("_"+str(subregister) if subregister else ""),"delete",id) |
return ApiController.delete(self,ver, register, subregister, id,id2) | return ApiController.delete(self,ver, register, subregister, id,id2) |
def search(self, ver=None, register=None): | def search(self, ver=None, register=None): |
id = None | id = None |
try: | try: |
params = MultiDict(self._get_search_params(request.params)) | params = MultiDict(self._get_search_params(request.params)) |
if 'q' in params.keys(): | if 'q' in params.keys(): |
id = params['q'] | id = params['q'] |
if 'query' in params.keys(): | if 'query' in params.keys(): |
id = params['query'] | id = params['query'] |
except ValueError, e: | except ValueError, e: |
print str(e) | print str(e) |
pass | pass |
self._post_analytics(c.user,register,"search",id) | self._post_analytics(c.user,register,"search",id) |
{% ckan_extends %} | |
{% block meta %} | |
{{ super() }} | |
<meta name="google-site-verification" content="B8scmW2_LMm4IW_ogXK9lojznO57GBpYlWrFjfGz3X8" /> | |
{% endblock %} | |
{% ckan_extends %} | |
{% block scripts %} | |
{{ super() }} | |
<script> | |
preload_resource.url = preload_resource.url.replace("https:","").replace("http:",""); | |
preload_resource.original_url = preload_resource.original_url.replace("https:","").replace("http:",""); | |
</script> | |
{% endblock %} | |
<div class="module-content ckanext-datapreview"> | |
{% if embed %} | |
{# images can be embedded directly #} | |
<img src="{{ resource_url }}"></img> | |
{% else %} | |
<div class="data-viewer-error js-hide"> | |
<p class="text-error"> | |
<i class="icon-info-sign"></i> | |
{{ _('This resource can not be previewed at the moment.') }} | |
<a href="#" data-toggle="collapse" data-target="#data-view-error"> | |
{{ _('Click here for more information.') }} | |
</a> | |
</p> | |
<p id="data-view-error" class="collapse"></p> | |
<p> | |
<a href="{{ raw_resource_url }}" class="btn btn-large resource-url-analytics" target="_blank"> | |
<i class="icon-large icon-download"></i> | |
{{ _('Download resource') }} | |
</a> | |
</p> | |
</div> | |
<iframe src="{{ resource_url }}" frameborder="0" width="100%" data-module="data-viewer"> | |
<p>{{ _('Your browser does not support iframes.') }}</p> | |
</iframe> | |
{% endif %} | |
</div> | |
<div class="embedhint" style="padding-left: 15px;"> | |
<h4>Embed this visualisation in your own website...</h4> | |
Copy the HTML in the box below and you can display this visualisation on your own website.<br/> | |
<textarea style="width:97%"><iframe src="{{ resource_url }}" frameborder="0" width="100%" height="600px"></iframe></textarea> | |
</div> | |
{% ckan_extends %} | {% ckan_extends %} |
{% block primary_content_inner %} | {% block primary_content_inner %} |
{{ super() }} | {{ super() }} |
<div class="module-content"> | <div class="module-content"> |
{{ h.disqus_comments() }} | {{ h.disqus_comments() }} |
</div> | </div> |
{% endblock %} | {% endblock %} |
{% block package_additional_info %} | {% block package_additional_info %} |
<section class="additional-info" prefix="dc: http://purl.org/dc/elements/1.1/ dcat: http://www.w3.org/ns/dcat# | <section class="additional-info" prefix="dc: http://purl.org/dc/elements/1.1/ dcat: http://www.w3.org/ns/dcat# |
dcam: http://purl.org/dc/dcam/ | dcam: http://purl.org/dc/dcam/ |
aglsterms: http://www.agls.gov.au/agls/terms/ | aglsterms: http://www.agls.gov.au/agls/terms/ |
agentterms: http://www.agls.gov.au/agls/agentterms/ | agentterms: http://www.agls.gov.au/agls/agentterms/ |
availterms: http://www.agls.gov.au/agls/availterms/ | availterms: http://www.agls.gov.au/agls/availterms/ |
adminterms: http://www.agls.gov.au/agls/adminterms/ | adminterms: http://www.agls.gov.au/agls/adminterms/ |
dct: http://purl.org/dc/terms/"> | dct: http://purl.org/dc/terms/"> |
<h3>{{ _('Additional Info') }}</h3> | <h3>{{ _('Additional Info') }}</h3> |
<table class="table table-striped table-bordered table-condensed"> | <table class="table table-striped table-bordered table-condensed"> |
<thead> | <thead> |
<tr> | <tr> |
<th scope="col">{{ _('Field') }}</th> | <th scope="col">{{ _('Field') }}</th> |
<th scope="col">{{ _('Value') }}</th> | <th scope="col">{{ _('Value') }}</th> |
</tr> | </tr> |
</thead> | </thead> |
<tbody> | <tbody> |
{# Add our custom field to the dataset read page. #} | {# Add our custom field to the dataset read page. #} |
{% if pkg.get('metadata_created') %} | {% if pkg.get('metadata_created') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Date Published</th> | <th scope="row" class="dataset-label">Date Published</th> |
<td class="dataset-details" property="dct:issued">{{ pkg.metadata_created.split("T")[0] }}</td> | <td class="dataset-details" property="dct:issued">{{ pkg.metadata_created.split("T")[0] }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('metadata_updated') %} | {% if pkg.get('metadata_updated') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Date Updated</th> | <th scope="row" class="dataset-label">Date Updated</th> |
<td class="dataset-details" property="dct:updated">{{ pkg.metadata_updated.split("T")[0] }}</td> | <td class="dataset-details" property="dct:updated">{{ pkg.metadata_updated.split("T")[0] }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('agency_program') %} | {% if pkg.get('agency_program') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Agency Program</th> | <th scope="row" class="dataset-label">Agency Program</th> |
<td class="dataset-details"> {{ pkg.agency_program }}</td> | <td class="dataset-details"> {{ pkg.agency_program }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('url') %} | {% if pkg.get('url') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">{{ _('Source') }}</th> | <th scope="row" class="dataset-label">{{ _('Source') }}</th> |
<td class="dataset-details" property="dct:source">{{ h.link_to(pkg.get('url'), | <td class="dataset-details" property="dct:source">{{ h.link_to(pkg.get('url'), |
pkg.get('url'), rel='dct:source', target='_blank') }} | pkg.get('url'), rel='dct:source', target='_blank') }} |
</td> | </td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('contact_point') %} | {% if pkg.get('contact_point') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Contact Point</th> | <th scope="row" class="dataset-label">Contact Point</th> |
<td class="dataset-details" property="dcat:contactPoint"> {{ h.mail_to(email_address=pkg.contact_point, | <td class="dataset-details" property="dcat:contactPoint"> {{ h.mail_to(email_address=pkg.contact_point, |
name=pkg.contact_point) }}</td> | name=pkg.contact_point) }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('spatial_coverage') %} | {% if pkg.get('spatial_coverage') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Geospatial Coverage</th> | <th scope="row" class="dataset-label">Geospatial Coverage</th> |
<td class="dataset-details" property="dct:spatial"> {{ pkg.spatial_coverage }}</td> | <td class="dataset-details" property="dct:spatial"> {{ pkg.spatial_coverage }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('granularity') %} | {% if pkg.get('granularity') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Data Granularity</th> | <th scope="row" class="dataset-label">Data Granularity</th> |
<td class="dataset-details"> {{ pkg.granularity }}</td> | <td class="dataset-details"> {{ pkg.granularity }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('jurisdiction') %} | {% if pkg.get('jurisdiction') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Government Jurisdiction</th> | <th scope="row" class="dataset-label">Government Jurisdiction</th> |
<td class="dataset-details" property="aglsterms:AglsJuri"> {{ pkg.jurisdiction }}</td> | <td class="dataset-details" property="aglsterms:AglsJuri"> {{ pkg.jurisdiction }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('temporal_coverage') %} | {% if pkg.get('temporal_coverage') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Temporal Coverage</th> | <th scope="row" class="dataset-label">Temporal Coverage</th> |
<td class="dataset-details" property="dct:temporal"> {{ pkg.temporal_coverage }}</td> | <td class="dataset-details" property="dct:temporal"> {{ pkg.temporal_coverage }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('data_state') %} | {% if pkg.get('data_state') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Data State</th> | <th scope="row" class="dataset-label">Data State</th> |
<td class="dataset-details"> {{ pkg.data_state }}</td> | <td class="dataset-details"> {{ pkg.data_state }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% if pkg.get('update_freq') %} | {% if pkg.get('update_freq') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">Update Frequency</th> | <th scope="row" class="dataset-label">Update Frequency</th> |
<td class="dataset-details"> {{ pkg.update_freq }}</td> | <td class="dataset-details"> {{ pkg.update_freq }}</td> |
</tr> | </tr> |
{% endif %} | {% endif %} |
{% set email =h.get_last_active_user(c.pkg_dict['id']).get('email','') %} | {% set email =h.get_last_active_user(c.pkg_dict['id']).get('email','') %} |
<tr> | <tr> |
<th scope="row" class="dataset-label">{{ _('Maintainer') }}</th> | <th scope="row" class="dataset-label">{{ _('Maintainer') }}</th> |
<td class="dataset-details" property="dc:contributor">{{ | <td class="dataset-details" property="dc:contributor">{{ |
h.mail_to(email_address=(email or ' '), name=h.get_last_active_user(c.pkg_dict['id']).get("display_name",'')) }} | h.mail_to(email_address=(email or ' '), name=h.get_last_active_user(c.pkg_dict['id']).get("display_name",'')) }} |
</td> | </td> |
</tr> | </tr> |
<tr> | <tr> |
<th scope="row" class="dataset-label">Publisher/Agency</th> | <th scope="row" class="dataset-label">Publisher/Agency</th> |
<td class="dataset-details" property="dc:publisher">{{ c.pkg_dict['organization']['title']}} | <td class="dataset-details" property="dc:publisher">{{ c.pkg_dict['organization']['title']}} |
</td> | </td> |
</tr> | </tr> |
<tr> | <tr> |
<th scope="row" class="dataset-label">Type</th> | <th scope="row" class="dataset-label">Type</th> |
<td class="dataset-details" property="dc:type">Dataset</td> | <td class="dataset-details" property="dc:type">Dataset</td> |
</tr> | </tr> |
<tr> | <tr> |
<th scope="row" class="dataset-label">Language</th> | <th scope="row" class="dataset-label">Language</th> |
<td class="dataset-details" property="dc:language">English</td> | <td class="dataset-details" property="dc:language">English</td> |
</tr> | </tr> |
</tbody> | </tbody> |
</table> | </table> |
</section> | </section> |
{% endblock %} | {% endblock %} |
{% block secondary_content %} | |
{{ super() }} | |
{% set dataset_extent = h.get_pkg_dict_extra(c.pkg_dict, 'spatial', '') %} | |
{% if dataset_extent %} | |
{% snippet "spatial/snippets/dataset_map_sidebar.html", extent=dataset_extent %} | |
{% endif %} | |
{% endblock %} | |