Skip to content
Open
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
37 changes: 36 additions & 1 deletion ckanext/scheming/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def lang():
from ckantoolkit import h
return h.lang()

@helper
def scheming_composite_separator():
return config.get('scheming.composite.separator','-')

@helper
def scheming_language_text(text, prefer_lang=None):
Expand Down Expand Up @@ -420,16 +423,48 @@ def scheming_flatten_subfield(subfield, data):
If data already contains flattened subfields (e.g. rendering values
after a validation error) then they are returned as-is.
"""
from ckantoolkit import h
flat = dict(data)
sep = h.scheming_composite_separator()

if subfield['field_name'] not in data:
return flat

for i, record in enumerate(data[subfield['field_name']]):
prefix = '{field_name}-{index}-'.format(
prefix = '{field_name}{sep}{index}{sep}'.format(
field_name=subfield['field_name'],
index=i,
sep=sep,
)
for k in record:
flat[prefix + k] = record[k]
return flat

@helper
def scheming_flatten_simple_subfield(subfield, data):
"""
Return flattened_data that converts all nested data for this subfield
into {field_name}-{subfield_name} values at the top level,
so that it matches the names of form fields submitted.

If data already contains flattened subfields (e.g. rendering values
after a validation error) then they are returned as-is.
"""
from ckantoolkit import h
flat = dict(data)
sep = h.scheming_composite_separator()

if subfield['field_name'] not in data:
return flat

subdata = data[subfield['field_name']]
if(isinstance(subdata, list) and len(subdata) == 1):
subdata = subdata[0]

for field, value in subdata.items():
prefix = '{field_name}{sep}'.format(
field_name=subfield['field_name'],
sep=sep,
)
flat[prefix + field] = value
return flat
76 changes: 65 additions & 11 deletions ckanext/scheming/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ def validate(self, context, data_dict, schema, action):

if before:
schema['__before'] = validation.validators_from_string(
before, None, scheming_schema)
before, None, scheming_schema) + validation.validators_from_string('scheming_simple_subfields', None, scheming_schema)
else:
schema['__before'] = validation.validators_from_string('scheming_simple_subfields', None, scheming_schema)
if after:
schema['__after'] = validation.validators_from_string(
after, None, scheming_schema)
Expand All @@ -262,7 +264,7 @@ def validate(self, context, data_dict, schema, action):
scheming_schema,
convert_this
)
if convert_this and 'repeating_subfields' in f:
if convert_this and ('repeating_subfields' in f or 'simple_subfields' in f):
composite_convert_fields.append(f['field_name'])

def composite_convert_to(key, data, errors, context):
Expand All @@ -284,6 +286,14 @@ def composite_convert_to(key, data, errors, context):
if ex['key'] not in composite_convert_fields
]
else:
dataset_simple_composite = {
f['field_name']
for f in scheming_schema['dataset_fields']
if 'simple_subfields' in f
}
if dataset_simple_composite:
expand_form_simple_composite(data_dict, dataset_simple_composite)

dataset_composite = {
f['field_name']
for f in scheming_schema['dataset_fields']
Expand Down Expand Up @@ -331,15 +341,16 @@ def expand_form_composite(data, fieldnames):
when submitting dataset/resource form composite fields look like
"field-0-subfield..." convert these to lists of dicts
"""
sep = p.toolkit.h.scheming_composite_separator()
# if "field" exists, don't look for "field-0-subfield"
fieldnames -= set(data)
if not fieldnames:
return
indexes = {}
for key in sorted(data):
if '-' not in key:
if sep not in key:
continue
parts = key.split('-')
parts = key.split(sep)
if parts[0] not in fieldnames:
continue
if parts[1] not in indexes:
Expand All @@ -348,16 +359,43 @@ def expand_form_composite(data, fieldnames):
parts[1] = indexes[parts[1]]
try:
try:
comp[int(parts[1])]['-'.join(parts[2:])] = data[key]
comp[int(parts[1])][sep.join(parts[2:])] = data[key]
del data[key]
except IndexError:
comp.append({})
comp[int(parts[1])]['-'.join(parts[2:])] = data[key]
comp[int(parts[1])][sep.join(parts[2:])] = data[key]
del data[key]
except (IndexError, ValueError):
pass # best-effort only

def expand_form_simple_composite(data, fieldnames):
"""
when submitting dataset/resource form composite fields look like
"field-subfield..." convert these to lists of dicts
"""
sep = p.toolkit.h.scheming_composite_separator()

# if "field" exists, don't look for "field-subfield"
fieldnames -= set(data)
if not fieldnames:
return
for key in sorted(data):
if sep not in key:
continue
parts = key.split(sep)
if parts[0] not in fieldnames:
continue
comp = data.setdefault(parts[0], [])
try:
try:
comp[0][sep.join(parts[1:])] = data[key]
del data[key]
except IndexError:
comp.append({})
comp[0][sep.join(parts[1:])] = data[key]
del data[key]
except (IndexError, ValueError):
pass # best-effort only

class SchemingGroupsPlugin(p.SingletonPlugin, _GroupOrganizationMixin,
DefaultGroupForm, _SchemingMixin):
Expand Down Expand Up @@ -445,12 +483,13 @@ def before_dataset_index(self, data_dict):
for d in schemas[data_dict['type']]['dataset_fields']:
if d['field_name'] not in data_dict:
continue
if 'repeating_subfields' in d:
if 'simple_subfields' in d and isinstance(data_dict[d['field_name']], list):
data_dict[d['field_name']] = data_dict[d['field_name']][0]
if 'repeating_subfields' in d or 'simple_subfields' in d:
data_dict[d['field_name']] = json.dumps(data_dict[d['field_name']])

return data_dict


def _load_schemas(schemas, type_field):
out = {}
for n in schemas:
Expand Down Expand Up @@ -516,7 +555,12 @@ def _field_output_validators(f, schema, convert_extras,
"""
Return the output validators for a scheming field f
"""
if 'repeating_subfields' in f:
if 'simple_subfields' in f:
validators = {
sf['field_name']: _field_output_validators(sf, schema, False)
for sf in f['simple_subfields']
}
elif 'repeating_subfields' in f:
validators = {
sf['field_name']: _field_output_validators(sf, schema, False)
for sf in f['repeating_subfields']
Expand Down Expand Up @@ -551,7 +595,12 @@ def _field_validators(f, schema, convert_extras):

# If this field contains children, we need a special validator to handle
# them.
if 'repeating_subfields' in f:
if 'simple_subfields' in f:
validators = {
sf['field_name']: _field_validators(sf, schema, False)
for sf in f['simple_subfields']
}
elif 'repeating_subfields' in f:
validators = {
sf['field_name']: _field_validators(sf, schema, False)
for sf in f['repeating_subfields']
Expand Down Expand Up @@ -579,7 +628,12 @@ def _field_create_validators(f, schema, convert_extras):

# If this field contains children, we need a special validator to handle
# them.
if 'repeating_subfields' in f:
if 'simple_subfields' in f:
validators = {
sf['field_name']: _field_create_validators(sf, schema, False)
for sf in f['simple_subfields']
}
elif 'repeating_subfields' in f:
validators = {
sf['field_name']: _field_create_validators(sf, schema, False)
for sf in f['repeating_subfields']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<div class="panel-body">
<dl class="scheming-subfield-list">
{% for subfield in field.repeating_subfields %}
{% if subfield.field_name in field_data and field_data[subfield.field_name]|length and subfield.display_snippet is not none %}
<dt class="dataset-label">
{{ h.scheming_language_text(subfield.label) }}
</dt>
Expand All @@ -20,6 +21,7 @@
object_type=object_type
-%}
</dd>
{% endif %}
{% endfor %}
</dl>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% set fields = data[field.field_name] %}
{% block subfield_display %}
<div class="panel panel-default panel-simple-composite">
<div class="panel-body">
{% for field_data in fields %}
<dl class="scheming-subfield-list">
{% for subfield in field.simple_subfields %}
{% if subfield.field_name in field_data and field_data[subfield.field_name]|length and subfield.display_snippet is not none %}
<dt class="dataset-label">
{{ h.scheming_language_text(subfield.label) }}
</dt>
<dd>
{%- snippet 'scheming/snippets/display_field.html',
field=subfield,
data=field_data,
entity_type=entity_type,
object_type=object_type
-%}
</dd>
{% endif %}
{% endfor %}
</dl>
{% endfor %}
</div>
</div>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
{% for subfield in field.repeating_subfields %}
{% set sf = dict(
subfield,
field_name=field.field_name ~ '-' ~ index ~ '-' ~ subfield.field_name)
field_name=field.field_name ~ h.scheming_composite_separator() ~ index ~ h.scheming_composite_separator() ~ subfield.field_name)
%}
{%- snippet 'scheming/snippets/form_field.html',
field=sf,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{# A complex field with simple sub-fields #}


{% import 'macros/form.html' as form %}
{% include 'scheming/snippets/subfields_asset.html' %}
{% macro repeating_panel(index, index1) %}
<div class="scheming-subfield-group" data-field="{{ field.field_name }}" data-group-index="index">
<div class="panel panel-default">
<div class="panel-body fields-content">
{% for subfield in field.simple_subfields %}
{% set sf = dict(
subfield,
field_name=field.field_name ~ h.scheming_composite_separator() ~ subfield.field_name)
%}
{%- snippet 'scheming/snippets/form_field.html',
field=sf,
data=flat,
errors=flaterr,
licenses=licenses,
entity_type=entity_type,
object_type=object_type
-%}
{% endfor %}
</div>
<div class="panel-body fields-removed-notice" style="display:none">
{% block removal_text %}
{% if 'id' in data %}
{{ _('These fields have been removed, click update below to save your changes.') }}
{% else %}
{{ _('These fields have been removed.') }}
{% endif %}
{% endblock %}
</div>
</div>
</div>
{% endmacro %}

{% set flat = h.scheming_flatten_simple_subfield(field, data) %}
{% set flaterr = h.scheming_flatten_simple_subfield(field, errors) %}

{% call form.input_block(
'field-' + field.field_name,
h.scheming_language_text(field.label) or field.field_name,
[],
field.classes if 'classes' in field else ['control-medium'],
dict({"class": "form-control"}, **(field.get('form_attrs', {}))),
is_required=h.scheming_field_required(field)) %}
<fieldset name="scheming-simple-subfields" class="scheming-fieldset" data-module="scheming-simple-subfields">
{% set alert_warning = h.scheming_language_text(field.form_alert_warning) %}
{% if alert_warning %}
<section class="alert alert-warning">
{{ alert_warning|safe }}
</section>
{% endif %}

<div class="scheming-simple-subfields-group">
{{ repeating_panel(0, 1) }}
</div>
<div class="control-medium">
{% set help_text = h.scheming_language_text(field.help_text) %}
{% if help_text %}
<div class="info-block mrgn-tp-md">
{{ help_text }}
</div>
{% endif %}
</div>

</fieldset>
{% endcall %}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
{%- if not display_snippet -%}
{%- if field.repeating_subfields -%}
{%- set display_snippet = 'repeating_subfields.html' -%}
{%- elif field.simple_subfields -%}
{%- set display_snippet = 'simple_subfields.html' -%}
{%- elif h.scheming_field_choices(field) -%}
{%- set display_snippet = 'select.html' -%}
{%- else -%}
Expand Down
32 changes: 32 additions & 0 deletions ckanext/scheming/templates/scheming/snippets/errors.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,38 @@
</li>
{%- endif -%}
{%- endfor -%}
{%- elif 'simple_subfields' in field %}
{%- for se in errors -%}
{%- if se -%}
<li data-field-label="{{ field.field_name }}">{{
h.scheming_language_text(field.label) }}:
<ul>
{%- for sf in field.simple_subfields -%}
{%- set se_unprocessed = se.copy() -%}

{%- if 'error_snippet' in sf -%}
{%- set sfe_snippet = sf.error_snippet -%}

{%- if '/' not in sfe_snippet -%}
{%- set sfe_snippet = 'scheming/error_snippets/' +
sfe_snippet -%}
{%- endif -%}

{%- snippet sfe_snippet, unprocessed=se_unprocessed,
field=sf, fields=field.simple_subfileds,
entity_type=entity_type, object_type=object_type -%}
{%- endif -%}

{%- if sf.field_name in se_unprocessed -%}
<li data-field-label="{{ field.field_name }}{{ h.scheming_composite_separator() }}{{ sf.field_name }}">{{
h.scheming_language_text(sf.label) }}:
{{ se_unprocessed[sf.field_name][0] }}</li>
{%- endif -%}
{%- endfor -%}
</ul>
</li>
{%- endif -%}
{%- endfor -%}
{%- else -%}
<li data-field-label="{{ field.field_name }}">{{
h.scheming_language_text(field.label) }}:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{#- master snippet for all scheming form fields -#}
{#- render the field the user requested, or use a default field -#}
{%- set form_snippet = field.form_snippet|default(
'repeating_subfields.html' if field.repeating_subfields else 'text.html') -%}
'repeating_subfields.html' if field.repeating_subfields else 'simple_subfields.html' if field.simple_subfields else 'text.html') -%}

{%- if '/' not in form_snippet -%}
{%- set form_snippet = 'scheming/form_snippets/' + form_snippet -%}
Expand Down
Loading