Skip to main content

ckanext-actor-registry

PyPI

Reusable publishers and contact points for CKAN metadata.

Status: 0.3.1 alpha. This version is intended for evaluation and feedback. It has an automated test suite that runs against CKAN 2.11 and CKAN 2.12. The user interface is in English by default, with a Swedish translation catalog following the installation's locale settings. Do not treat it as production-ready without your own review and tests.

Documentation

Why this extension exists

CKAN datasets often repeat publisher and contact details. Repetition is easy to introduce but expensive to maintain: a changed email address or organization name must be corrected on every affected dataset.

This extension stores publishers and contact points once and lets datasets reference them by stable internal IDs. Registry changes are resolved when a dataset is displayed or exported, so one central update can be reflected wherever the record is used. This isn't just a convenience: DCAT-AP itself expects a dataset's publisher and contact point to be addressable entities in their own right, not text -- see "Data model and DCAT-AP mapping" below for how the registry implements that.

A publisher is an organization or a person that publishes datasets. A contact point is a reusable contact function; it can belong to an organization (a publisher) but doesn't have to. Neither concept replaces a CKAN organization: CKAN organizations continue to control dataset ownership and permissions.

Terminology. In the interface these records are called publishers. The word actor survives only in technical places: the URLs (/actors), the dataset field publisher_actor_id, the Python package and the action names (actor_registry_*).

Publishers vs. CKAN organizations

CKAN already has an "Organization" concept, so it's fair to ask why this extension adds new records instead of extending it.

A CKAN organization is primarily an access-control and ownership unit: every dataset belongs to exactly one organization, and membership in it determines who may create, edit or delete the dataset. That's a different question from who publishes a dataset or who to contact about it. The two often coincide in practice, but not always: a dataset can be maintained by editors who aren't themselves the responsible publisher, a single dataset commonly needs more than one contact point (technical, legal, general enquiries) where an organization has only the one dataset-to-organization relationship, a publisher can be a person rather than an organization at all, and the same publisher can publish datasets that live under different owner organizations on the same catalog -- none of which map cleanly onto extending the organization model.

Extending CKAN's organization type to carry this metadata would also mean changing a core concept that already governs permissions, for a purely descriptive need. Publishers and contact points are deliberately independent instead: additive tables with no CKAN user account, group membership or permission of their own, and no effect on how organizations already control dataset ownership.

Choosing your fields: organization, publisher and contact points

You do not have to use both registries. Which fields a dataset has is decided by the ckanext-scheming schema you configure. The extension does not add or remove a field in the schema; the optional snippets for CKAN's own contact fields (see "Migrate to Actor Registry") can hide Maintainer and Author on forms and pages, and only if your schema uses them.

Why choose deliberately. CKAN core stores contact details as free text on each dataset (Author, Author email, Maintainer, Maintainer email). When the same contact appears on many datasets, every copy has to be kept up to date separately, and copies drift apart. The organization, on the other hand, is mainly about ownership and who may edit, and is often also the right publisher, but not always (see above). So the two needs are different:

Your catalogue Use Example schema
The owner organization is the publisher; contact details are repeated across datasets Organization + contact points, no publisher field contact_points_only_schema.yaml
The publisher can differ from the owner organization (another organization, a person) Organization + publisher + contact points actor_registry_schema.yaml

Catalogues differ more than one expects: the organization can be a department while the city is the publisher, or the other way round. Common setups describes the setups we know of and how to handle each one.

Both ship in ckanext/actor_registry/examples/ and are exercised by the test suite. Configure one of them (they both define the dataset type), or copy one into your own schema.

How CKAN's own fields map:

CKAN core With the registry
Organization (owner_org) unchanged: ownership and permissions
Author, Author email, Maintainer, Maintainer email replaced by Contact points (contact_point_ids) if your schema uses that field instead
(no equivalent) Publisher (publisher_actor_id), optional

What happens to CKAN's own fields. Nothing, by the extension. Without Scheming the registry still works (its pages, lists and merging), but no dataset fields appear and CKAN's form is unchanged. With Scheming, the form and dataset page show exactly the fields in your schema, as with any Scheming schema: the example schemas leave out Author, Maintainer, licence, tags and version to stay short, so those do not appear. Add them back to your schema if you need them, for example during a transition. Values already stored on existing datasets stay in the database and in the API; they are only not shown when the schema lacks the field, and they are not copied into the registry automatically (see "Activating on an existing installation with datasets" below).

In the optional DCAT export (ckanext-dcat with this extension's profile): a dataset without a publisher record is exported with its organization as dct:publisher (ckanext-dcat's own fallback), and with its registry contact points as dcat:contactPoint; without those, ckanext-dcat falls back to Maintainer and then Author. A dataset with registry contact points never exports Maintainer or Author as well, and the setting ckanext.actor_registry.legacy_contact_fields = false stops exporting them altogether (see Installation). The stored values are never changed.

What visitors see is what is exported

A ground rule for this extension: the dataset page is designed first, and the DCAT export follows what the page shows, never the other way round. The page and the export use the same resolution of publishers and contact points:

  • an inactive publisher or contact point is still what the dataset says until someone replaces it, so it is shown and exported (the forms no longer offer it; see Cleanup);
  • a merged publisher is replaced by the one it was merged into, on both;
  • a reference to a record that no longer exists is on neither;
  • CKAN's own Maintainer and Author are exported only as far as the page shows them: never when the dataset has registry contact points or the migration is closed, and otherwise only the fields your schema shows (without Scheming, CKAN's own page shows all of them). A shown Author is exported as dct:creator, which DCAT-AP-SE calls producent.

Deliberate exceptions, decided and documented:

  • No publisher: the page shows no publisher (the organization is already shown on the page), while ckanext-dcat exports the organization as dct:publisher.

Features

  • central registries for publishers and contact points;
  • stable URIs, identifiers (unique per identifier scheme) and optional controlled publisher types;
  • active/inactive records without breaking existing references; merging of duplicates and, for sysadmins, permanent deletion;
  • alphabetical selectors with previews;
  • inline creation and editing while editing a dataset, available to any logged-in editor;
  • the current user's last publisher preselected on a new dataset;
  • the current user's three most recently used contact points shown first;
  • readable display on the dataset page (name and contact details, never internal IDs), and a page per publisher and contact point that lists its datasets;
  • a worklist of datasets that are missing a publisher and/or a contact point;
  • one shared validation layer (lengths, email, web addresses, URIs, phone numbers) and opt-in validators that keep datasets from pointing at records that no longer exist;
  • Scheming helpers, form and display snippets, and a localised example schema;
  • an English interface with Swedish (sv, sv_SE) translations;
  • optional DCAT integration: publishers become foaf:Agent values and contact points become vcard:Kind values;
  • optional vcard:hasTelephone serialization for the European DCAT-AP 3 profile (a vcard:Voice with a tel: URI, the usual vCard form);
  • validation that prevents a publisher and a contact point from sharing the same RDF URI.

Data model and DCAT-AP mapping

None of this matters if you don't use DCAT: the RDF profile is entirely opt-in (enabled only by setting ckanext.dcat.rdf.profiles = actor_registry_euro_dcat_ap_3 after installing ckanext-dcat). Without it, publishers and contact points work exactly like a plain metadata-management feature -- centrally maintained records, picked from a dropdown, editable in place -- and you never need to think about foaf:Agent or vcard:Kind. The rest of this section only matters once RDF/DCAT-AP export enters the picture.

DCAT-AP does not treat a dataset's publisher or contact point as free text: dct:publisher is expected to be a foaf:Agent with its own identity, and dcat:contactPoint a vcard:Kind -- both addressable, reusable entities, not strings duplicated on every dataset. This extension's registry gives CKAN real, database-backed records with stable URIs for exactly that purpose; publisher_actor_id / contact_point_ids on the dataset are resolved into the foaf:Agent / vcard:Kind RDF shape only when a dataset is serialized for DCAT export -- nothing is duplicated onto the dataset itself.

A publisher's Kind (Organization or Person) determines the specific agent type used on export. A contact point's phone number is additionally serialized as a vcard:hasTelephone / vcard:Voice node carrying a tel: URI, the usual vCard form. ckanext-dcat itself does not export phone numbers. The extension builds on ckanext-dcat's EuropeanDCATAP3Profile, so a DCAT-AP 3 consumer gets correctly typed, dereferenceable publisher and contact point data without extra configuration.

Screenshots

Everything below follows from one idea: publishers and contact points are maintained once, centrally, instead of being retyped on every dataset -- which is manageable for a handful of datasets and increasingly unmanageable as a catalog grows.

The screenshots are being retaken for 0.2.0. Each image link below is marked; the images will be added in a later commit.

Publishers: organizations and people

A publisher can represent either an organization or a person -- the Kind field is the only difference, and everything else in the registry (the dataset pickers, the quick-create widget, merging) treats both the same way. An identifier and its scheme (for example SE:ORGNR) are optional but must be given together, and the pair is unique among active publishers: a clash names the existing publisher instead of failing with a generic error.

(Screenshot to be added later.)

(Screenshot to be added later.)

Contact points

Contact points are a separate, parallel register. A contact point can optionally belong to an organization ("Belongs to organization"), but doesn't have to -- a shared mailbox or a role-based contact works just as well as a named person.

(Screenshot to be added later.)

Linking a dataset to the registry

When editing a dataset, the publisher (a single publisher) and one or more contact points are chosen from the same registry used everywhere else in CKAN:

(Screenshot to be added later.)

If the publisher or contact point you need doesn't exist yet, either can be created without leaving the dataset form. And if one already exists but needs a correction, it can be edited from right here too -- the change applies everywhere it is used, not just on this dataset:

(Screenshot to be added later.)

What visitors see

On the public dataset page the publisher and each contact point are shown with their readable details -- name, web address, email and so on, one per line -- never internal IDs. A publisher that has been merged into another shows as the surviving publisher; inactive or missing records are simply left out.

(Screenshot to be added later.)

Publisher and contact point pages list the datasets they are linked to: the first ten, with the total and a "Show all" link to a paginated list. The lists respect dataset visibility, so private datasets are only shown to people who may see them.

(Screenshot to be added later.)

Merging two publishers

Merging two duplicate publishers -- select exactly two, choose which value wins per field, confirm, and the publisher that isn't kept is soft-retired and points to the remaining record. Datasets that pointed to the retired publisher are re-pointed automatically, so the surviving publisher's dataset count grows to reflect the merge:

(Screenshot to be added later.)

Soft-retiring rather than deleting exists for one reason: a merged publisher's web address (its URI) may already be linked to, or cached, outside this catalogue -- in a harvested copy, an external DCAT graph, a bookmark. A retired publisher's own page keeps responding and points at the record it was merged into, instead of going dead. Retired publishers are hidden from /actors by default so the list stays focused on what's still in use, with a checkbox to show them again. From there, a sysadmin can also permanently delete one -- useful for cleaning up test data or duplicates that were never referenced outside this catalogue -- but doing so gives up that permanent-address guarantee for good, which is why the extension asks for that decision explicitly rather than doing it automatically after a merge.

A dataset with no publisher set

A newly created dataset with neither a publisher nor a contact point shows an inline warning on its own page, with pickers for both fields. This is also exactly what an editor sees, dataset by dataset, right after the extension is activated on a catalog that already has data -- see "Activating on an existing installation with datasets" below for the full picture, including the separate worklist view:

(Screenshot to be added later.)

The same dataset also shows up on the registry's own worklist, so it can be found and fixed from either place:

(Screenshot to be added later.)

Saving resolves the warning immediately, without leaving the page:

(Screenshot to be added later.)

Requirements and compatibility

  • CKAN 2.11.1 or later 2.11 releases, or CKAN 2.12; the full automated suite (including the official DCAT-AP 3 SHACL validation) runs on all of them;
  • PostgreSQL, as required by CKAN;
  • ckanext-scheming for the included dataset form widgets; 3.1.0 is recommended (see docs/INSTALLATION.md for the versions that are tested and a known deprecation warning on CKAN 2.12);
  • ckanext-dcat and RDFLib only when the optional RDF profile is enabled.

CKAN 2.11.0 and 2.10 and older are not supported (2.11.0 could not be tested: its official image has no working pytest plugin).

Installation

The quickest way to try the extension is the Docker Compose example. For an existing CKAN, follow docs/INSTALLATION.md; in short, install the package from PyPI in the same Python environment as CKAN:

pip install ckanext-actor-registry

To work on the extension, install a source checkout instead: pip install -e . in the checkout.

Add actor_registry to ckan.plugins:

ckan.plugins = ... actor_registry

Create or upgrade the extension's database tables:

ckan -c /etc/ckan/default/ckan.ini db upgrade -p actor_registry

Restart CKAN. Logged-in users can then manage publishers at /actors and contact points at /contactpoints.

Scheming integration

The extension supplies choice helpers, two form snippets, two display snippets and two optional validators. A minimal schema configuration is:

- field_name: publisher_actor_id
  label: Publisher
  preset: select
  required: true
  form_snippet: actors_multiple_select.html
  display_snippet: actor.html
  choices_helper: actors_choices
  sorted_choices: true
  validators: scheming_required actor_registry_actor_exists

- field_name: contact_point_ids
  label: Contact points
  preset: multiple_select
  form_snippet: contactpoints_multiple_select.html
  display_snippet: contactpoints.html
  choices_helper: contactpoints_choices
  sorted_choices: true
  validators: ignore_missing scheming_multiple_choice actor_registry_contactpoints_exist

A complete, localised (en, sv, sv_SE) example schema ships with the extension and is used by the tests: ckanext.actor_registry.examples:actor_registry_schema.yaml. It is an integration example, not a universal metadata schema.

The field names shown above are part of the 0.x data contract. Datasets store registry UUIDs in these fields (publisher_actor_id as a string, contact_point_ids as a list). The display snippets resolve them to readable details on the dataset page. For RDF export the profile resolves them into the publisher and contact shapes that the Scheming profiles in ckanext-dcat expect, at serialization time only; nothing extra is written onto the dataset. The two actor_registry_*_exist validators are opt-in: they make a dataset refuse a reference to a record that does not exist or is no longer active.

Optional DCAT profile

The core registry is independent of DCAT, country and organization type. For RDF export with telephone support, enable the profile entry point after installing ckanext-dcat:

ckanext.dcat.rdf.profiles = actor_registry_euro_dcat_ap_3

The profile subclasses EuropeanDCATAP3Profile. It delegates normal publisher and contact serialization to ckanext-dcat and adds telephone values as vcard:hasTelephone / vcard:Voice / vcard:hasValue with a tel: URI. Theme and publisher-type URIs are also typed as skos:Concept so standalone DCAT-AP 3 validation can identify their intended range. Store publisher types as controlled-vocabulary URIs rather than local display labels. This is an interoperability aid, not a claim that installing this extension alone makes a catalog conformant with DCAT-AP or a national profile based on it. Conformance also depends on the complete dataset schema, validation and profile configuration.

Access control and data handling

  • registry list and edit pages, and inline publisher/contact creation, are available to any logged-in CKAN user, not just sysadmins -- the registry holds shared, catalog-wide data meant for editors generally;
  • merging two publishers is the one exception and stays restricted to CKAN sysadmins, since it is destructive and re-points datasets across the whole catalog rather than just the acting user's own organization;
  • deployments must set ckan.csrf_protection.ignore_extensions = false so CKAN validates the CSRF tokens included by the management and inline forms;
  • individual publisher and contact point display pages are readable wherever the CKAN site itself permits access;
  • input is validated on the server by one shared layer, for the forms, the inline dialogs and the merge action alike: length limits that match the database columns, email addresses (checked with CKAN's own validator), web addresses, URIs and phone numbers. Stored web addresses and URIs are only rendered as links when CKAN's h.is_url accepts them; anything else is shown as plain text;
  • per-user convenience data stores only CKAN user IDs, the most recent publisher ID and recently used contact point IDs;
  • the dataset lists on a publisher's or contact point's page, on their "Show all" pages and in the /actors worklist only include private datasets that the visitor may see: sysadmins, and members of the dataset's organization. The totals shown follow the same rule, so a private dataset is never revealed by a count. FYI: a user who was given access to a single private dataset through CKAN's dataset collaborators feature (ckan.auth.allow_dataset_collaborators, off by default) but is not a member of its organization does not see that dataset in these lists; the dataset itself can still be opened directly;
  • a publisher can also be permanently deleted (/actors/<id>/delete), sysadmin- only like merging. Datasets that had it as publisher have that field cleared; contact points it owned become standalone rather than being deleted themselves. This is a real delete, not a soft-retire -- see "Merging two publishers" above for why that distinction matters;
  • a contact point can likewise be permanently deleted (/contactpoints/<id>/delete), sysadmin-only. It is removed from the contact_point_ids of any dataset that listed it (other contact points on the same dataset are left alone); an owning publisher, if any, is unaffected.

Activating on an existing installation with datasets

Enabling this extension on a CKAN instance that already has datasets does not retroactively link anything: every existing dataset starts out with no publisher and no contact point in the registry, because those are new, separate fields the extension adds via Scheming (see "Scheming integration" above). Three features exist specifically to help you find and fix these datasets afterwards; none runs automatically, so plan to use them as a one-time cleanup pass after activation.

Migrate to Actor Registry (/actor-registry/migrate). If your datasets already hold contact details in CKAN's own free-text fields, this page turns them into shared contact points. It reads Maintainer, and Author only when a dataset has no maintainer, on the datasets that have no contact point yet, and groups them by email address (or by name when there is no valid address), showing the spellings used and any existing contact point with the same address. For each group you create a contact point, link to an existing one, or skip; clear the datasets that belong elsewhere and they stay for a later round. One email address may back several contact points. The page shows how many datasets are linked, skipped and remaining.

While the old fields are still in use, editors may keep changing them. When Maintainer or Author changes on a dataset after it was linked, the page lists it under Changed since linking, with the old and new values, and you choose: update the contact point (for every dataset that uses it), link this dataset to another contact point, create a new one, or keep it as it is.

Closing the migration. When you are done, a sysadmin closes the migration on the same page. Maintainer and Author are then hidden in forms and on dataset pages, for everyone including sysadmins, and are no longer exported; their values stay stored, and saving a dataset keeps them. It takes effect at once, without a restart, and the migration can be reopened. If datasets are still without a contact point, or have unhandled changes, the page says how many and asks you to confirm. Closing sets ckanext.actor_registry.legacy_contact_fields to false at runtime (see Installation).

Hiding needs one thing in your schema: give the old fields the extension's legacy_contact.html snippets, which show them while the migration is open:

- field_name: maintainer
  label: Maintainer
  form_snippet: legacy_contact.html
  display_snippet: legacy_contact.html
# ... and the same for maintainer_email, author and author_email

A schema without these fields has nothing to hide. A theme that overrides Scheming's additional_info.html block itself must leave out rows with display_snippet: legacy_contact.html while the migration is closed, as the extension's own version does.

The original fields are never changed: linking only sets the contact points, through a partial update that checks package_update for each dataset. A dataset that fails your schema's validation is reported and the others are still linked. Editors see and migrate only the datasets they may edit; sysadmins all of them. Every decision is logged in the actor_registry_migration_decision table (run ckan db upgrade -p actor_registry).

A worklist on /actors. The registry management page (Publishers) lists the active datasets that are missing something, with a direct link to each one. By default it shows the datasets missing both a publisher and any contact point ("Datasets missing both a publisher and a contact point (N)"), which surfaces the datasets nobody has touched yet. The Missing: buttons above the list widen or change that: Publisher (every dataset without a publisher), Contact point (every dataset without a contact point) or Either (missing at least one of the two). The page shows the first ten with the total and a "Show all" link to a paginated list, and private datasets only appear for people who may see them.

An inline fix on the dataset's own page. Opening any dataset that is missing a publisher and/or a contact point shows a warning box near the top of the page with the same selector widgets (including "create new...") used in the full dataset edit form, so editors recognize the control instead of having to find the right field inside the advanced edit view. Saving it performs a partial update -- only the field(s) you actually filled in are touched, the rest of the dataset is left alone -- and requires the normal package_update permission for that dataset, not sysadmin. The prompt follows your schema: it asks for a contact point when the schema has that field, and for a publisher only when the schema requires one.

Deactivating and cleaning up. Deactivating a publisher or contact point stops new use of it: it is no longer offered in the forms, and choosing it anyway gives a message that names it. Datasets that already use it keep it and can still be saved; the form shows it marked inactive instead of dropping it. The Cleanup page (/actor-registry/cleanup, a tab, also hinted on the registry pages) lists the inactive records still in use, and datasets that point to records that no longer exist, and replaces or removes such a reference on the datasets you may edit (editors their own, sysadmins all; a merged publisher suggests the one it was merged into).

All three rely on ckanext-scheming, so they only cover dataset types managed by Scheming (the migration only those whose schema has the contact points field).

Upgrade note for the prototype

The migration history retains the table name contactpoints_alembic_version. This is intentional so installations upgraded from the earlier, unpublished ckanext-contactpoints prototype preserve their existing data. New installations should use only the actor_registry plugin and migration command.

Upgrading from the 0.1.0 source snapshot? See the "Upgrading" section of the 0.2.0 release notes: run the migration, and note that the translation keys changed with the UI wording.

Known limitations

  • no Action API is provided for registry administration;
  • there is no import/export command for registry data;
  • browser-based (end-to-end) tests and a formal security review are still pending;
  • selection and validation of identifier schemes and publisher-type vocabularies is left to the deploying catalog;
  • the dataset lists on publisher and contact point pages do not include private datasets that a user can only see as a dataset collaborator (see "Access control and data handling").

See CHANGELOG.md for release contents and CONTRIBUTING.md for how to help. Please report security issues according to SECURITY.md.

The automated and manual test strategy is documented in TESTING.md.

Development transparency

The implementation and documentation were developed by Björn Hagström with assistance from OpenAI's ChatGPT and Codex and, from version 0.2.0, from Anthropic's Claude Code. The human maintainer directs, reviews and accepts responsibility for the released work, including its correctness, security, licensing and provenance. See AI_ASSISTANCE.md.

License

MIT. See LICENSE.

Metadata

Release files for ckanext-actor-registry 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ckanext-actor-registry 0.3.1
File Size Uploaded
ckanext_actor_registry-0.3.1.tar.gz 229.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ckanext-actor-registry 0.3.1
File Interpreter ABI Platform
ckanext_actor_registry-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 468.9 kB

Release files / ckanext_actor_registry-0.3.1.tar.gz

Download URL ckanext_actor_registry-0.3.1.tar.gz
Size 229.9 kB
Tags Source
SHA-256 checksum
How to use checksums
24da12914fb4d6cc2cdbce1c3bdbbee9f41ee469861a99553f28766398888c8f
BLAKE2b-256 checksum
How to use checksums
042c2bef8187cbb540dea9dbf44dd08e9cd912cec701307ad4c09c9d5fef5162
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / ckanext_actor_registry-0.3.1-py3-none-any.whl

Download URL ckanext_actor_registry-0.3.1-py3-none-any.whl
Size 239.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
20b6637cdc66d1d51d55641b2ed9671ee71822f237cdc99d940583c1a11d281a
BLAKE2b-256 checksum
How to use checksums
51a13963e159c5fa97ef54e7f9ad3bcaa494edfaf71d13370d6cf04568741f9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

0.4.1

2 release files

0.4.0

2 release files

This release

0.3.1 This release

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page