Commit c153e78e authored by Klaus Zimmermann's avatar Klaus Zimmermann
Browse files

Remove editor (closes #207)

parent 401e81e1
[submodule "cf-index-meta"]
path = cf-index-meta
url = https://bitbucket.org/cf-index-meta/cf-index-meta.git
cf-index-meta @ 0646d702
Subproject commit 0646d702614f512bb10a6b83a70a110a0e0119a7
from .main import main
from .._version import get_versions
__version__ = get_versions()['version']
del get_versions
__all__ = [
'__version__',
'main',
]
# -*- coding: utf-8 -*-
import argparse
from contextlib import contextmanager
import logging
from jinja2 import Environment, PackageLoader
import climix
from .mastertable import build_index_definitions, build_variables
def add_space(variable, quote=False):
value = variable.strip()
if len(value) > 0:
if quote:
return ' "{}"'.format(value)
else:
return ' {}'.format(value)
else:
return value
def prepare_environment(args):
loader = PackageLoader('climix.editor')
env = Environment(
loader=loader,
trim_blocks=True,
)
env.filters['add_space'] = add_space
return env
def parse_args():
parser = argparse.ArgumentParser(
description=(f'An editor for a climate index thing, '
f'version {climix.__version__}.'))
parser.add_argument('-f', '--force', action='store_true')
parser.add_argument('-t', '--table-version', default='<unknown>')
parser.add_argument('document')
return parser.parse_args()
@contextmanager
def opened_w_force(filename, force):
try:
f = open(filename, 'x')
except FileExistsError:
if force:
logging.warning('File {} already exists. '
'Overwriting due to --force'.format(filename))
f = open(filename, 'w')
else:
raise
try:
yield f
finally:
f.close()
def main():
args = parse_args()
env = prepare_environment(args)
var_definition_template = env.get_template('variables.yml')
var_definitions = build_variables(args.document)
var_output = var_definition_template.render(variables=var_definitions,
version=args.table_version)
idx_definition_template = env.get_template('index_definitions.yml')
idx_definitions = build_index_definitions(args.document)
idx_output = idx_definition_template.render(indices=idx_definitions,
version=args.table_version)
with opened_w_force('variables.yml', args.force) as outfile:
outfile.write(var_output)
with opened_w_force('index_definitions.yml', args.force) as outfile:
outfile.write(idx_output)
# -*- coding: utf-8 -*-
import logging
import regex as re
import pyexcel as pe
from climix.index_functions.support import (SUPPORTED_OPERATORS,
SUPPORTED_REDUCERS)
def build_periods(spec):
PERIODS = {
'ann': 'annual',
'sea': 'seasonal',
'mon': 'monthly',
'_': 'unknown',
'': 'unknown',
}
periods = [PERIODS[period] for period in spec.split('/')]
return periods
def tr_cell_methods(cell_method_string):
name = r'(?P<name>\w+):'
method = (r'(?P<method>('
r'point|sum|maximum|maximum_absolute_value|median|'
r'mid_range|minimum|minimum_absolute_value|mean|'
r'mean_absolute_value|mean_of_upper_decile|mode|'
r'range|root_mean_square|standard_deviation|'
r'sum_of_squares|variance))')
where = r'where'
type1 = r'(?P<type1>\w+)'
type2 = r'(?P<type2>\w+)'
clim_indicator = r'(?P<indicator>(within|over))'
clim_unit = r'(?P<unit>(days|years))'
cell_method = re.compile(
f'({name} )+{method}'
f'(( {where} {type1}( over {type2})?)|'
f'( {clim_indicator} {clim_unit}))?')
cms = [m.group(0) for m in cell_method.finditer(cell_method_string)]
return cms
def split_parts(no_parts, part_string):
if no_parts == 0:
return []
parts = [p.strip() for p in part_string.split(',')]
assert len(parts) == no_parts
return parts
def tr_inputs(input):
inputs = {}
for input_variable in input.split(','):
key, variable = input_variable.split(':')
inputs[key.strip()] = variable.strip()
return inputs
def tr_parameter(parameter):
if parameter['operator'] is not None:
d = {'var_name': parameter['name'],
'kind': 'operator',
'operator': parameter['operator']}
elif parameter['reducer'] is not None:
d = {'var_name': parameter['name'],
'kind': 'reducer',
'reducer': parameter['reducer']}
elif parameter['value'] is not None:
d = {'var_name': parameter['name'],
'kind': 'quantity',
'standard_name': parameter['standard_name'],
'proposed_standard_name': parameter['proposed_standard_name'],
'data': parameter['value'],
'units': parameter['units'],
'long_name': parameter['long_name']}
else:
raise RuntimeError(f"Invalid parameter found {parameter[0]}")
return d
def split_parameter_definitions(parameter_definitions_string, parameter_names):
name_regex = r'(?P<name>{})'.format('|'.join(parameter_names))
op_regex = r'(?P<operator>{})'.format('|'.join(SUPPORTED_OPERATORS))
red_regex = r'(?P<reducer>{})'.format('|'.join(SUPPORTED_REDUCERS))
qty_regex = (
r'\(var_name: (?P<var_name>[^,]*), '
r'standard_name: (?P<standard_name>[^,]*), '
r'(proposed_standard_name: (?P<proposed_standard_name>[^,]*), )?'
r'value: (?P<value>[^,]*), '
r'unit: (?P<units>[^,)]*)(, |\))'
r'(long_name: \p{Pi}(?P<long_name>[^\p{Pf}]*)\p{Pf}\))?')
param_regex = r'{}: (?:{}|{}|{})'.format(
name_regex, red_regex, op_regex, qty_regex
)
matcher = re.compile(param_regex)
result = [tr_parameter(p)
for p in matcher.finditer(parameter_definitions_string)]
return result
def tr_index_function(index_name, name, no_thresholds,
parameter_names_string, parameter_definitions_string):
parameter_names = split_parts(no_thresholds, parameter_names_string)
parameters = split_parameter_definitions(parameter_definitions_string,
parameter_names)
found_parameters = set(p['var_name'] for p in parameters)
if found_parameters != set(parameter_names):
logging.warn(f"For index {index_name}, the parameters listed in "
f"parameter_name ({parameter_names}) are different from "
f"those defined in PARAMETER_definition "
f"({found_parameters}). Please check the table!")
index_function = {
'name': name,
'parameters': parameters,
}
return index_function
def prepare_record(record):
var_name = record['VarName']
no_parameters = int(record['N_parameters'])
d = {
'var_name': var_name,
'reference': record['OUTPUT_reference'],
'period': {'allowed': build_periods(record['allowed_freq']),
'default': build_periods(record['default_freq'])[0]},
'output': {
'var_name': var_name,
'standard_name': record['OUTPUT_standard_name'],
'proposed_standard_name': record['OUTPUT_proposed_standard_name'],
'long_name': record['OUTPUT_long_name'],
'cell_methods': tr_cell_methods(record['OUTPUT_cell_methods']),
'units': record['OUTPUT_user_units'],
},
'inputs': tr_inputs(record['input']),
'index_function': tr_index_function(
var_name,
record['index_function'],
no_parameters,
record['parameter_name'], record['PARAMETER_definition']),
'ET': {
'short_name': record['ET_short_name'],
'long_name': record['ET_long_name'],
'definition': record['ET_definition'],
'comment': record['ET_comment'],
}
}
proposed_standard_name = record['OUTPUT_proposed_standard_name']
if proposed_standard_name.strip() != '':
d['output']['proposed_standard_name'] = proposed_standard_name
return d
def build_index_definitions(file_name):
sheet = pe.get_sheet(file_name=file_name,
sheet_name='index_definitions')
sheet.name_columns_by_row(0)
records = sheet.to_records()
index_definitions = []
for record in records:
try:
ready = int(record['ready'])
except ValueError:
ready = -1
if ready != 1:
continue
index_definitions.append(prepare_record(record))
return index_definitions
def prepare_variable_record(record):
var_name = record['var_name'].strip()
d = {
'var_name': var_name,
'standard_name': record['standard_name'].strip(),
'cell_methods': tr_cell_methods(record['cell_methods']),
'aliases': [a.strip() for a in record['aliases'].split(',')],
'comment': record['comment'].strip()
}
return d
def build_variables(file_name):
sheet = pe.get_sheet(file_name=file_name,
sheet_name='variables')
sheet.name_columns_by_row(0)
records = sheet.to_records()
variables = []
for record in records:
if record['var_name'].strip() == '':
continue
variables.append(prepare_variable_record(record))
return variables
# These index definitions are auto-generated from the master table at
# https://bitbucket.org/cf-index-meta/cf-index-meta
# This is based on version {{ version }}.
---
indices:
{% for idx in indices %}
{{ idx.var_name }}:
reference: {{- idx.reference|add_space }}
period:
allowed:
{% for p in idx.period.allowed %}
{{ p }}:
{% endfor %}
default: {{- idx.period.default|add_space }}
output:
var_name: {{- idx.output.var_name|add_space(quote=True) }}
standard_name: {{- idx.output.standard_name|add_space }}
{% if idx.output.proposed_standard_name %}
proposed_standard_name: {{- idx.output.proposed_standard_name|add_space }}
{% endif %}
long_name: {{- idx.output.long_name|add_space(quote=True) }}
units: {{- idx.output.units|add_space(quote=True) }}
cell_methods:
{% for cm in idx.output.cell_methods %}
- {{ cm }}
{% endfor %}
input:
{% for argname, variable in idx.inputs.items() %}
{{ argname }}: {{- variable|add_space }}
{% endfor %}
index_function:
name: {{- idx.index_function.name|add_space }}
parameters:
{% for param in idx.index_function.parameters %}
{{ param.var_name }}:
kind: {{- param.kind|add_space }}
{% if param.kind == 'quantity' %}
standard_name: {{- param.standard_name|add_space }}
{% if param.proposed_standard_name is not none %}
proposed_standard_name: {{- param.proposed_standard_name|add_space }}
{% endif %}
{% if param.long_name is not none %}
long_name: {{- param.long_name|add_space(quote=True) }}
{% endif %}
data: {{- param.data|add_space }}
units: {{- param.units|add_space(quote=True) }}
{% elif param.kind == 'operator' %}
operator: {{- param.operator|add_space(quote=True) }}
{% elif param.kind == 'reducer' %}
reducer: {{- param.reducer|add_space }}
{% else %}
# Warning: Unknown kind!
{% endif %}
{% endfor %}
ET:
short_name: {{- idx.ET.short_name|add_space(quote=True) }}
long_name: {{- idx.ET.long_name|add_space(quote=True) }}
definition: {{- idx.ET.definition|add_space(quote=True) }}
comment: {{- idx.ET.comment|add_space(quote=True) }}
{% if not loop.last %}
{% endif %}
{% endfor %}
# These variables are auto-generated from the master table at
# https://bitbucket.org/cf-index-meta/cf-index-meta
# This is based on version {{ version }}.
---
variables:
{% for var in variables %}
{{ var.var_name }}:
standard_name: {{ var.standard_name }}
cell_methods:
{% for cm in var.cell_methods %}
- {{ cm }}
{% endfor %}
aliases:
{% for alias in var.aliases %}
- {{ alias }}
{% endfor %}
{% if var.comment != '' %}
comment: {{ var.comment }}
{% endif %}
{% endfor %}
...@@ -44,13 +44,9 @@ setuptools.setup( ...@@ -44,13 +44,9 @@ setuptools.setup(
'scitools-iris>=2.2.0,<3', 'scitools-iris>=2.2.0,<3',
'sparse', 'sparse',
], ],
extras_require={
'editor': ['pyexcel', 'pyexcel-xls', 'jinja2']
},
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'climix=climix.main:main', 'climix=climix.main:main',
'climix-editor=climix.editor:main [editor]',
], ],
'climix.index_functions': [ 'climix.index_functions': [
'count_level_crossings=' 'count_level_crossings='
......
#!/bin/bash
SCRIPT_DIR="$(dirname $(readlink -f "$0"))"
BASE_DIR="$SCRIPT_DIR/.."
TABLE_DIR="$BASE_DIR/cf-index-meta"
OUTPUT_DIR="$BASE_DIR/climix/etc"
(cd $BASE_DIR && git submodule update --remote)
TABLE_VERSION=$(cd $TABLE_DIR && git describe --dirty)
(cd $OUTPUT_DIR &&
climix-editor -t "$TABLE_VERSION" -f $TABLE_DIR/master_table.xls)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment