From: Alex Sadleir Date: Mon, 06 Jan 2014 12:30:12 +0000 Subject: Initial commit X-Git-Url: http://maxious.lambdacomplex.org/git/?p=ckanext-agls.git&a=commitdiff&h=dad93a955f5a1aa839a26964201838d8f11c815e --- Initial commit --- --- /dev/null +++ b/.gitignore @@ -1,1 +1,10 @@ - +*.egg-info +*.pyc +*.swp +*.swo +*~ +#* +.#* +build/ +dist/ +distribute-* --- /dev/null +++ b/README.rst @@ -1,1 +1,17 @@ +This CKAN Extension customises a CKAN instance for the hosting of data.gov.au. +It comprises: + + * A path to local customisations of the core templates to include AGLS/Dublin Core minimum metadata + * A custom n3/rdf output format + +Installation +============ + +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 + +Then activate it by setting ``ckan.plugins = datagovau`` in your main ``ini``-file. + + --- /dev/null +++ b/ckanext/__init__.py @@ -1,1 +1,8 @@ +# this is a namespace package +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__) --- /dev/null +++ b/ckanext/agls/__init__.py --- /dev/null +++ b/ckanext/agls/plugin.py @@ -1,1 +1,128 @@ +import logging +import ckan.plugins as plugins +import ckan.lib as lib +import ckan.lib.dictization.model_dictize as model_dictize +import ckan.plugins.toolkit as tk +import ckan.model as model +from pylons import config + +from sqlalchemy import orm +import ckan.model + +#parse the activity feed for last active non-system user +def get_last_active_user(id): + system_user = lib.helpers.get_action('user_show',{'id': config.get('ckan.site_id', 'ckan_site_user')}) + user_list = [x for x in lib.helpers.get_action('package_activity_list',{'id':id}) if x['user_id'] != system_user['id']] + user = None + if len(user_list) > 0: + user = user_list[0].get('user_id', None) + if user is None: + return system_user + else: + return lib.helpers.get_action('user_show',{'id':user}) + +class AGLSPlugin(plugins.SingletonPlugin, + tk.DefaultDatasetForm): + '''An example IDatasetForm CKAN plugin. + + Uses a tag vocabulary to add a custom metadata field to datasets. + + ''' + plugins.implements(plugins.IConfigurer, inherit=False) + plugins.implements(plugins.IDatasetForm, inherit=False) + plugins.implements(plugins.ITemplateHelpers, inherit=False) + + def update_config(self, config): + # Add this plugin's templates dir to CKAN's extra_template_paths, so + # that CKAN will use this plugin's custom templates. + # here = os.path.dirname(__file__) + # rootdir = os.path.dirname(os.path.dirname(here)) + + tk.add_template_directory(config, 'templates') + tk.add_public_directory(config, 'theme/public') + tk.add_resource('theme/public', 'ckanext-agls') + # config['licenses_group_url'] = 'http://%(ckan.site_url)/licenses.json' + + def get_helpers(self): + return {'get_last_active_user': get_last_active_user} + + def is_fallback(self): + # Return True to register this plugin as the default handler for + # package types not handled by any other IDatasetForm plugin. + return True + + def package_types(self): + # This plugin doesn't handle any special package types, it just + # registers itself as the default (above). + return [] + + + def create_package_schema(self): + schema = super(AGLSPlugin, self).create_package_schema() + schema = self._modify_package_schema(schema) + return schema + + def update_package_schema(self): + schema = super(AGLSPlugin, self).update_package_schema() + schema = self._modify_package_schema(schema) + return schema + + def show_package_schema(self): + schema = super(AGLSPlugin, self).show_package_schema() + + # Don't show vocab tags mixed in with normal 'free' tags + # (e.g. on dataset pages, or on the search page) + schema['tags']['__extras'].append(tk.get_converter('free_tags_only')) + + # Add our custom_text field to the dataset schema. + # ignore_missing == optional + # ignore_empty == mandatory but not for viewing + # !!! always convert_from_extras first + schema.update({ + 'agency_program': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_missing')], + 'contact_point': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')], + 'spatial_coverage': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')], + 'granularity': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')], + 'jurisdiction': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')], + 'temporal_coverage': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')], + 'data_state': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')], + 'update_freq': [tk.get_converter('convert_from_extras'), + tk.get_validator('ignore_empty')] + }) + return schema + + def _modify_package_schema(self, schema): + # Add our custom_test metadata field to the schema, this one will use + # convert_to_extras instead of convert_to_tags. + # ignore_missing == optional + # not_empty == mandatory, enforced here while modifying + + schema.update({ + 'agency_program': [tk.get_validator('ignore_missing'), + tk.get_converter('convert_to_extras')], + 'contact_point': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')], + 'spatial_coverage': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')], + 'granularity': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')], + 'jurisdiction': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')], + 'temporal_coverage': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')], + 'data_state': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')], + 'update_freq': [tk.get_converter('convert_to_extras'), + tk.get_validator('not_empty')] + }) + return schema + + --- /dev/null +++ b/ckanext/agls/templates/package/read.html @@ -1,1 +1,124 @@ +{% ckan_extends %} +{% block package_additional_info %} +
+

{{ _('Additional Info') }}

+ + + + + + + + + + {# Add our custom field to the dataset read page. #} + {% if pkg.get('metadata_created') %} + + + + + {% endif %} + {% if pkg.get('metadata_updated') %} + + + + + {% endif %} + {% if pkg.get('agency_program') %} + + + + + {% endif %} + {% if pkg.get('url') %} + + + + + {% endif %} + + {% if pkg.get('contact_point') %} + + + + + {% endif %} + {% if pkg.get('spatial_coverage') %} + + + + + + {% endif %} + {% if pkg.get('granularity') %} + + + + + + {% endif %} + {% if pkg.get('jurisdiction') %} + + + + + {% endif %} + {% if pkg.get('temporal_coverage') %} + + + + + {% endif %} + {% if pkg.get('data_state') %} + + + + + {% endif %} + {% if pkg.get('update_freq') %} + + + + + + {% endif %} + +{% set email =h.get_last_active_user(c.pkg_dict['id']).get('email','') %} + + + + + + + + + + + + + + + + + + +
{{ _('Field') }}{{ _('Value') }}
Date Published{{ pkg.metadata_created.split("T")[0] }}
Date Updated{{ pkg.metadata_updated.split("T")[0] }}
Agency Program {{ pkg.agency_program }}
{{ _('Source') }}{{ h.link_to(pkg.get('url'), + pkg.get('url'), rel='dct:source', target='_blank') }} +
Contact Point {{ h.mail_to(email_address=pkg.contact_point, + name=pkg.contact_point) }}
Geospatial Coverage {{ pkg.spatial_coverage }}
Data Granularity {{ pkg.granularity }}
Government Jurisdiction {{ pkg.jurisdiction }}
Temporal Coverage {{ pkg.temporal_coverage }}
Data State {{ pkg.data_state }}
Update Frequency {{ pkg.update_freq }}
{{ _('Maintainer') }}{{ + h.mail_to(email_address=(email or ' '), name=h.get_last_active_user(c.pkg_dict['id']).get("display_name",'')) }} +
Publisher/Agency{{ c.pkg_dict['organization']['title']}} +
TypeDataset
LanguageEnglish
+
+ + +{% endblock %} + + --- /dev/null +++ b/ckanext/agls/templates/package/read.rdf @@ -1,1 +1,133 @@ + + + + ${c.pkg_dict['name']} + ${c.pkg_dict['title']} + + + Dataset + ${c.pkg_dict['notes']} + ${c.pkg_dict['metadata_created']} + ${c.pkg_dict['metadata_modified']} + + + + + + + ${c.pkg_dict['license_id']} + "${c.pkg_dict['license_id']}" + + + + ${ tag_dict["name"] } + + + + + + + ${rsc_dict.get('name')} + + ${rsc_dict.get('description')} + + ${rsc_dict.get('created')} + ${rsc_dict.get('revision_timestamp')} + ${rsc_dict.get('size')} + ${rsc_dict.get('mimetype')} + ${rsc_dict.get('size')} bytes + + + ${rsc_dict.get('format')} + ${rsc_dict.get('format')} + + + ${rsc_dict.get('name')} + + + + + + + + + ${ c.pkg_dict['organization']['title'] } + + + + + ${ c.pkg_dict['organization']['title'] } + + + + + ${h.get_last_active_user(c.pkg_dict['id'])["display_name"]} + + + + + + + + ${extra_dict.get('key','')} + ${extra_dict.get('value','')} + + + + en + + ${c.pkg_dict.contact_point } + + + ${ c.pkg_dict.spatial } + + + ${ c.pkg_dict.spatial_coverage } + + + ${ c.pkg_dict.jurisdiction } + ${ c.pkg_dict.get('temporal_coverage') } + + + Data State + ${ c.pkg_dict.get('data_state') } + + + + + Update Frequency + ${ c.pkg_dict.get('update_freq') } + + + + + Agency Program + ${ c.pkg_dict.get('agency_program') } + + + + + Data Granularity + ${ c.pkg_dict.get('granularity') } + + + + + --- /dev/null +++ b/ckanext/agls/templates/package/read_base.html @@ -1,1 +1,47 @@ +{% ckan_extends %} +{% block links -%} +{{ super() }} + + + +{% endblock -%} + +{% block head_extras -%} +{{ super() }} + + + + + + + + + + + + + + + + +{% endblock -%} + +{% block package_info %} +
+
+

{{ pkg.title or pkg.name }}

+
+
+
{{ _('Followers') }}
+
{{ h.SI_number_span(h.get_action('dataset_follower_count', {'id': pkg.id})) }}
+
+
+ +
+
+{% endblock %} + + --- /dev/null +++ b/ckanext/agls/templates/package/resource_read.html @@ -1,1 +1,35 @@ +{% ckan_extends %} +{% block links -%} +{{ super() }} + + + +{% endblock -%} + +{% block head_extras -%} +{{ super() }} + + + + + + + + + + + + + + + + + + + + +{% endblock -%} + + + --- /dev/null +++ b/ckanext/agls/templates/package/snippets/package_basic_fields.html @@ -1,1 +1,42 @@ +{% ckan_extends %} +{% block package_basic_fields_org %} + +{{ super() }} + + +{{ form.input('agency_program', label=_('Agency Program'), id='field-agency_program', +placeholder=_('Name of the Agency Program that generated the data if relevant'), +value=data.agency_program, error=errors.agency_program, classes=['control-medium']) }} + +{{ form.input('contact_point', label=_('Contact Point'), id='field-contact_point', +placeholder=_('Email address for questions about content of dataset'), +value=data.contact_point, error=errors.contact_point, classes=['control-medium']) }} + +{{ form.input('spatial_coverage', label=_('Geospatial Coverage'), id='field-spatial_coverage', +placeholder=_('The geographical area covered by the data. Eg: Whole of Australia, New South Wales, Canberra'), +value=data.spatial_coverage, error=errors.spatial_coverage, classes=['control-medium']) }} + +{{ form.input('granularity', label=_('Data Granularity'), id='field-granularity', +placeholder=_('Granularity in time, place etc. Eg: incidents reports on daily basis by location.'), +value=data.granularity, error=errors.granularity, classes=['control-medium']) }} + +{{ form.input('jurisdiction', label=_('Government Jurisdiction'), id='field-jurisdiction', +placeholder=_('New South Wales, Glenorchy City Council, Federal'), +value=data.jurisdiction, error=errors.jurisdiction, classes=['control-medium']) }} + +{{ form.input('temporal_coverage', label=_('Temporal Coverage'), id='field-temporal_coverage', +placeholder=_('The timespan the dataset relates to: Current, 2008, 2008-2012, Jan 2012'), +value=data.temporal_coverage, error=errors.temporal_coverage, classes=['control-medium']) }} + +{{ form.select('data_state', label=_('Data State'), options= [{'value': 'active', 'text': 'Active'}, {'value': 'inactive', 'text': 'Inactive'}], +selected='Active', error=errors.data_state) }} + +{{ form.input('update_freq', label=_('Update Frequency'), id='field-update_freq', +placeholder=_('How often the dataset is updated. Eg: Daily, Weekly, Never'), +value=data.update_freq, error=errors.update_freq, classes=['control-medium']) }} + +{% endblock %} + + + --- /dev/null +++ b/ckanext/agls/templates/package/snippets/package_metadata_fields.html @@ -1,1 +1,27 @@ +{% ckan_extends %} +{# Remove 'free extras' from the package form. If you're using +convert_to/from_extras() as we are with our 'custom_text' field below then +you need to remove free extras from the form, or editing your custom field +won't work. #} +{% block custom_fields %} +{% endblock %} + +{# https://github.com/okfn/ckan/blob/master/ckan/templates/macros/form.html documents the macros for fields #} + +{% block package_metadata_fields %} +{{ form.input('url', label=_('Source URL'), id='field-url', +placeholder=_('URL where dataset came from or more information can be obtained'), +value=data.url, error=errors.url, classes=['control-medium']) }} +{{ super() }} + +{% endblock %} + +{# hide author/maintainer fields #} +{% block package_metadata_author %} +{% endblock %} + +{% block package_metadata_fields_maintainer %} +{% endblock %} + + --- /dev/null +++ b/ckanext/agls/templates/package/snippets/resource_item.html @@ -1,1 +1,54 @@ +{% ckan_extends %} +{% set url = h.url_for(controller='package', action='resource_read', id=pkg.name, resource_id=res.id) %} +
  • + {% block resource_item_title %} + + {{ h.resource_display_name(res) | truncate(50) }}{{ res.format }} + {{ h.popular('views', res.tracking_summary.total, min=10) }} + + {% endblock %} +

    + {% if res.description %} + {{ h.markdown_extract(res.description, extract_length=80) }} + {% else %} + {{ _('No description for this resource') }} + {% endif %} +

    + {% block resource_item_explore %} + + {% endblock %} +
  • --- /dev/null +++ b/ckanext/agls/theme/public/licenses.json @@ -1,1 +1,30 @@ +[ + { + "domain_content": true, + "domain_data": false, + "domain_software": false, + "family": "", + "id": "other-open", + "is_generic": true, + "is_okd_compliant": true, + "is_osi_compliant": false, + "maintainer": "", + "status": "active", + "title": "Other (Open)", + "url": "" + }, + { + "domain_content": true, + "domain_data": false, + "domain_software": false, + "family": "", + "id": "cc-by", + "is_okd_compliant": true, + "is_osi_compliant": false, + "maintainer": "", + "status": "active", + "title": "Creative Commons Attribution 3.0 Australia", + "url": "http://creativecommons.org/licenses/by/3.0/au/" + } +] --- /dev/null +++ b/setup.py @@ -1,1 +1,27 @@ +from setuptools import setup, find_packages +version = '0.1' + +setup( + name='ckanext-agls', + version=version, + description='Extension for customising CKAN for data.gov.au', + long_description='', + classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + keywords='', + author='Alex Sadleir', + author_email='alex.sadleir@linkdigital.com.au', + url='', + license='', + packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), + namespace_packages=['ckanext', 'ckanext.agls'], + include_package_data=True, + zip_safe=False, + install_requires=[], + entry_points=\ + """ + [ckan.plugins] + agls=ckanext.agls.plugin:AGLSPlugin + """, +) +