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

Add the ability to create an arbitrary schema for rendering elsewhere #403

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
27 changes: 27 additions & 0 deletions ckanext/scheming/arbitrary_schema_example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
scheming_version: 2
schema_id: ckanext_notifier
about: An example of a config schema for a fictional extension

fields:
- field_name: ckanext.ckanext_notifier.enable_notifications
label: Enable notifications
validators: default(true) boolean_validator
preset: select
required: true
choices:
- value: true
label: Enable
- value: false
label: Disable

- field_name: ckanext.ckanext_notifier.notify_to_email
label: Notification email
validators: unicode_safe email_validator
required: true
help_text: Specify the email address to which the notification will be sent

- field_name: ckanext.ckanext_notifier.frequency
label: Notification frequency in seconds
validators: default(3600) int_validator
required: true
input_type: number
25 changes: 25 additions & 0 deletions ckanext/scheming/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,28 @@ def scheming_flatten_subfield(subfield, data):
for k in record:
flat[prefix + k] = record[k]
return flat


@helper
def scheming_arbitrary_schemas(expanded=True):
"""
Return the dict of arbitrary schemas. Or if scheming_arbitrary
plugin is not loaded return None.
"""
from ckanext.scheming.plugins import SchemingArbitraryPlugin as plugin

if plugin.instance:
if expanded:
return plugin.instance._expanded_schemas
return plugin.instance._schemas

return {}


@helper
def scheming_get_arbitrary_schema(schema_id, expanded=True):
"""
Return the schema for the schema_id passed or None if
no schema is defined for that schema_id.
"""
return scheming_arbitrary_schemas(expanded).get(schema_id)
11 changes: 11 additions & 0 deletions ckanext/scheming/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,17 @@ def get_actions(self):
logic.scheming_organization_schema_show,
}

class SchemingArbitraryPlugin(p.SingletonPlugin, _SchemingMixin):
p.implements(p.IConfigurer)

SCHEMA_OPTION = "scheming.arbitrary_schemas"
FALLBACK_OPTION = 'scheming.arbitrary_fallback'
SCHEMA_TYPE_FIELD = "schema_id"

@classmethod
def _store_instance(cls, self):
SchemingArbitraryPlugin.instance = self


class SchemingNerfIndexPlugin(p.SingletonPlugin):
"""
Expand Down
8 changes: 1 addition & 7 deletions ckanext/scheming/templates/scheming/group/group_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@
<form class="dataset-form form-horizontal" method="post" data-module="basic-form" enctype="multipart/form-data">
{{ h.csrf_input() if 'csrf_input' in h }}
{%- set schema = h.scheming_get_group_schema(group_type) -%}
{%- for field in schema['fields'] -%}
{%- if field.form_snippet is not none -%}
{%- snippet 'scheming/snippets/form_field.html',
field=field, data=data, errors=errors, licenses=licenses,
entity_type='group', object_type=group_type -%}
{%- endif -%}
{%- endfor -%}
{% snippet 'scheming/snippets/render_fields.html', fields=schema.fields, data=data, errors=errors, entity_type='group', object_type=group_type %}

<div class="form-actions">
{% block delete_button %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@
<form class="dataset-form form-horizontal" method="post" data-module="basic-form" enctype="multipart/form-data">
{{ h.csrf_input() if 'csrf_input' in h }}
{%- set schema = h.scheming_get_organization_schema(group_type) -%}
{%- for field in schema['fields'] -%}
{%- if field.form_snippet is not none -%}
{%- snippet 'scheming/snippets/form_field.html',
field=field, data=data, errors=errors, licenses=licenses,
entity_type='organization', object_type=group_type -%}
{%- endif -%}
{%- endfor -%}
{% snippet 'scheming/snippets/render_fields.html', fields=schema.fields, data=data, errors=errors, entity_type='organization', object_type=group_type %}

{{ form.required_message() }}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,30 +68,8 @@
{%- else -%}
{%- set fields = schema.dataset_fields -%}
{%- endif -%}
{%- for field in fields -%}
{%- if field.form_snippet is not none -%}
{%- if field.field_name not in data %}
{# Set the field default value before rendering but only if
it doesn't already exist in data which would mean the form
has been submitted. #}
{% if field.default_jinja2 %}
{% do data.__setitem__(
field.field_name,
h.scheming_render_from_string(field.default_jinja2)) %}
{% elif field.default %}
{% do data.__setitem__(field.field_name, field.default) %}
{% endif %}
{% endif -%}
{%- snippet 'scheming/snippets/form_field.html',
field=field,
data=data,
errors=errors,
licenses=c.licenses,
entity_type='dataset',
object_type=dataset_type
-%}
{%- endif -%}
{%- endfor -%}

{% snippet 'scheming/snippets/render_fields.html', fields=schema.dataset_fields, data=data, errors=errors, entity_type='dataset', object_type=dataset_type, set_fields_defaults=true %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like licenses isn't passed through here or in the resource_form

Copy link
Contributor Author

@mutantsan mutantsan Feb 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


{%- if pages -%}
<input type="hidden" name="_ckan_phase" value="{{ active_page }}" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,34 +47,7 @@
{%- endif -%}

{%- set schema = h.scheming_get_dataset_schema(dataset_type) -%}
{%- for field in schema.resource_fields -%}
{%- if field.form_snippet is not none -%}
{%- if field.field_name not in data %}
{# Set the field default value before rendering but only if
it doesn't already exist in data which would mean the form
has been submitted. #}
{% if field.default_jinja2 %}
{% do data.__setitem__(
field.field_name,
h.scheming_render_from_string(field.default_jinja2)) %}
{% elif field.default %}
{% do data.__setitem__(field.field_name, field.default) %}
{% endif %}
{% endif -%}
{# We pass pkg_name as the package_id because that's the only
variable available in this snippet #}
{%- snippet 'scheming/snippets/form_field.html',
field=field,
data=data,
errors=errors,
licenses=c.licenses,
entity_type='dataset',
object_type=dataset_type,
package_id=pkg_name
-%}
{%- endif -%}
{%- endfor -%}

{% snippet 'scheming/snippets/render_fields.html', fields=schema.resource_fields, data=data, errors=errors, entity_type='dataset', object_type=dataset_type, set_fields_defaults=true %}
wardi marked this conversation as resolved.
Show resolved Hide resolved
{% endblock %}


Expand Down
30 changes: 30 additions & 0 deletions ckanext/scheming/templates/scheming/snippets/render_fields.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{#}
fields - a list of scheming field dictionaries
data - form data fields
errors - A dict of errors for the fields
entity_type - entity type
object_type - object type
set_fields_defaults - flag to set the default field values
{#}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is cute but doing comments like this could lead to confusion about where the comment starts and ends

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


{% for field in fields if field.form_snippet is not none %}
{% if field.field_name not in data and set_fields_defaults %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the other templates are indented with 2 spaces. Better to be consistent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, thanks

{# Set the field default value before rendering but only if
it doesn't already exist in data which would mean the form
has been submitted. #}
{% if field.default_jinja2 %}
{% do data.__setitem__(field.field_name, h.scheming_render_from_string(field.default_jinja2)) %}
{% elif field.default %}
{% do data.__setitem__(field.field_name, field.default) %}
{% endif %}
{% endif %}

{% snippet 'scheming/snippets/form_field.html',
field=field,
data=data,
errors=errors,
licenses=licenses,
entity_type=entity_type,
object_type=object_type
%}
{% endfor %}
30 changes: 30 additions & 0 deletions ckanext/scheming/tests/test_arbitrary_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import pytest
from flask import render_template
from bs4 import BeautifulSoup

from ckanext.scheming.helpers import scheming_get_arbitrary_schema


class TestArbitrarySchema:
def test_arbitrary_schema_structure(self):
schema = scheming_get_arbitrary_schema("ckanext_notifier")

assert schema["scheming_version"]
assert schema["schema_id"] == "ckanext_notifier"
assert schema["about"]
assert isinstance(schema["fields"], list)

@pytest.mark.usefixtures("with_request_context")
def test_render_arbitrary_schema(self, app):
schema = scheming_get_arbitrary_schema("ckanext_notifier")

result = render_template(
"scheming/snippets/render_fields.html",
fields=schema["fields"],
data={},
errors={},
)

soup = BeautifulSoup(result)

assert len(soup.select("div.form-group")) == 3
Loading
Loading