Remove unused and unsupported table field

This commit removes any mention of table field from dynamic_ui and
removes floppyforms from requirements.

'table' field was introduced for MsSqlClusterServer service, that
changed significantly since then and does not use table field anymore.
See
https://github.com/murano-project/murano-app-incubator/blob/master/io.murano.windows.SqlServerCluster/UI/ui.yaml
for more details (even though the repository is deprecated).

Since no app available today uses this type of field, and no tests exist
for this type of field. There is no way to test, that it works correctly,
therefore it is very hard to transfer critical parts from floppyforms to
murano-dashboard.

'table' field depends on django-floppyforms, which is not
present in global-requirements, which is problematic and prevents us
from enabling proposal bot in murano-dashboard.

'table' field was never documented, so no documentation change is
required.

Change-Id: I638c722c2bdde411aaefa02f3d4027119de0e0bd
Closes-Bug: #1468342
This commit is contained in:
Kirill Zaitsev 2015-07-06 18:44:03 +03:00
parent 69c40fa433
commit b21538e2a3
10 changed files with 0 additions and 340 deletions

View File

@ -23,14 +23,11 @@ from django.core import validators as django_validator
from django import forms
from django.http import Http404
from django.template import defaultfilters
from django.template import loader
from django.utils import html
from django.utils.translation import ugettext_lazy as _
import floppyforms
from horizon import exceptions
from horizon import forms as hz_forms
from horizon import messages
from horizon import tables
from openstack_dashboard.api import glance
from openstack_dashboard.api import nova
import yaql
@ -278,183 +275,6 @@ class IntegerField(forms.IntegerField, CustomPropertiesField):
pass
class Column(tables.Column):
template_name = 'common/form-fields/data-grid/input.html'
def __init__(self, transform, table_name=None, **kwargs):
if hasattr(self, 'template_name'):
def _transform(datum):
context = {'data': getattr(datum, self.name, None),
'row_index': str(datum.id),
'table_name': table_name,
'column_name': self.name}
return loader.render_to_string(self.template_name, context)
_transform.__name__ = transform
transform = _transform
super(Column, self).__init__(transform, **kwargs)
class CheckColumn(Column):
template_name = 'common/form-fields/data-grid/checkbox.html'
class RadioColumn(Column):
template_name = 'common/form-fields/data-grid/radio.html'
# FixME: we need to have separated object until find out way to use the same
# code for MS SQL Cluster datagrid
class Object(object):
def __init__(self, id, **kwargs):
self.id = id
for key, value in kwargs.iteritems():
setattr(self, key, value)
def as_dict(self):
item = {}
for key, value in self.__dict__.iteritems():
if key != 'id':
item[key] = value
return item
def DataTableFactory(name, columns):
class Object(object):
row_name_re = re.compile(r'.*\{0}.*')
def __init__(self, id, **kwargs):
self.id = id
for key, value in kwargs.iteritems():
if isinstance(value, basestring) and \
re.match(self.row_name_re, value):
setattr(self, key, value.format(id))
else:
setattr(self, key, value)
class DataTableBase(tables.DataTable):
def __init__(self, request, data, **kwargs):
if len(data) and isinstance(data[0], dict):
objects = [Object(i, **item)
for (i, item) in enumerate(data, 1)]
else:
objects = data
super(DataTableBase, self).__init__(request, objects, **kwargs)
class Meta:
template = 'common/form-fields/data-grid/data_table.html'
name = ''
footer = False
attrs = dict((col_id, cls(col_id, verbose_name=col_name, table_name=name))
for (col_id, cls, col_name) in columns)
attrs['Meta'] = Meta
return tables.base.DataTableMetaclass('DataTable', (DataTableBase,), attrs)
class TableWidget(floppyforms.widgets.Input):
template_name = 'common/form-fields/data-grid/table_field.html'
delimiter_re = re.compile('([\w-]*)@@([0-9]*)@@([\w-]*)')
types = {'label': Column,
'radio': RadioColumn,
'checkbox': CheckColumn}
def __init__(self, columns_spec=None, table_class=None, js_buttons=True,
min_value=None, max_value=None, max_sync=None,
*args, **kwargs):
assert columns_spec is not None or table_class is not None
self.columns = []
if columns_spec:
for spec in columns_spec:
name = spec['column_name']
self.columns.append((name,
self.types[spec['column_type']],
spec.get('title', None) or name.title()))
self.table_class = table_class
self.js_buttons = js_buttons
self.min_value = min_value
self.max_value = max_value
self.max_sync = max_sync
# FixME: we need to use this hack because TableField passes all kwargs
# to TableWidget
for kwarg in ('widget', 'description', 'description_title'):
kwargs.pop(kwarg, None)
super(TableWidget, self).__init__(*args, **kwargs)
def get_context(self, name, value, attrs=None):
ctx = super(TableWidget, self).get_context_data()
if value:
if self.table_class:
cls = self.table_class
else:
cls = DataTableFactory(name, self.columns)
ctx.update({
'data_table': cls(self.request, value),
'js_buttons': self.js_buttons,
'min_value': self.min_value,
'max_value': self.max_value,
'max_sync': self.max_sync
})
return ctx
def value_from_datadict(self, data, files, name):
def extract_value(row_key, col_id, col_cls):
if col_cls == CheckColumn:
val = data.get("{0}@@{1}@@{2}".format(name, row_key, col_id),
False)
return val and val == 'on'
elif col_cls == RadioColumn:
row_id = data.get("{0}@@@@{1}".format(name, col_id), False)
return row_id == row_key
else:
return data.get("{0}@@{1}@@{2}".format(
name, row_key, col_id), None)
def extract_keys():
keys = set()
regexp = re.compile('^{name}@@([^@]*)@@.*$'.format(name=name))
for key in data.iterkeys():
match = re.match(regexp, key)
if match and match.group(1):
keys.add(match.group(1))
return keys
items = []
if self.table_class:
columns = [(_name, column.__class__, unicode(column.verbose_name))
for (_name, column)
in self.table_class.base_columns.items()]
else:
columns = self.columns
for row_key in extract_keys():
item = {}
for column_id, column_instance, column_name in columns:
value = extract_value(row_key, column_id, column_instance)
item[column_id] = value
items.append(Object(row_key, **item))
return items
class Media:
css = {'all': ('muranodashboard/css/tablefield.css',)}
class TableField(CustomPropertiesField):
def __init__(self, columns=None, label=None, table_class=None,
initial=None,
**kwargs):
widget = TableWidget(columns, table_class, **kwargs)
super(TableField, self).__init__(
label=label, widget=widget, initial=initial)
@with_request
def update(self, request, **kwargs):
self.widget.request = request
def clean(self, objects):
return [obj.as_dict() for obj in objects]
class ChoiceField(forms.ChoiceField, CustomPropertiesField):
pass

View File

@ -44,7 +44,6 @@ TYPES.update({
'password': fields.PasswordField,
'integer': fields.IntegerField,
'databaselist': fields.DatabaseListField,
'table': fields.TableField,
'flavor': fields.FlavorChoiceField,
'keypair': fields.KeyPairChoiceField,
'image': fields.ImageChoiceField,

View File

@ -8,7 +8,6 @@ DISABLED = False
ADD_INSTALLED_APPS = [
'muranodashboard',
'floppyforms'
]
ADD_EXCEPTIONS = {

View File

@ -1,10 +0,0 @@
input[type=radio].data-grid, input[type=checkbox].data-grid {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
width: auto !important;
}
.data-grid table {
margin-bottom: 5px ! important;
}

View File

@ -1,3 +0,0 @@
<input type="checkbox" class="data-grid"
{% if data %}checked="checked"{% endif %}
name="{{ table_name }}@@{{ row_index }}@@{{ column_name }}"/>

View File

@ -1,2 +0,0 @@
{% extends 'horizon/common/_data_table.html' %}
{% block table_caption %}{% endblock table_caption %}

View File

@ -1,4 +0,0 @@
{{ data }}
<input type="hidden" class="data-grid"
name="{{ table_name }}@@{{ row_index }}@@{{ column_name }}"
value="{{ data }}"/>

View File

@ -1,4 +0,0 @@
<input type="radio" class="data-grid"
{% if data %}checked="checked"{% endif %}
name="{{ table_name }}@@@@{{ column_name }}"
value="{{ row_index }}"/>

View File

@ -1,134 +0,0 @@
<div class="data-grid">{{ data_table.render }}</div>
{% if js_buttons %}
<button type="button" id="node-add" class="btn btn-small">Add node</button>
<button type="button" id="node-remove" class="btn btn-small">Remove node</button>
<script>
$(function() {
var MAX_NODES, MIN_NODES, MAX_SYNC;
if ( {{ max_value }} )
MAX_NODES = {{ max_value }};
else
MAX_NODES = Infinity;
if ( {{ min_value }} )
MIN_NODES = {{ min_value }};
else
MIN_NODES = -Infinity;
if ( {{ max_sync }} )
MAX_SYNC = {{ max_sync }};
else
MAX_SYNC = Infinity;
function trimLabel(label) {
return $.trim(label.replace(/(\r\n|\r|\n|↵)/gm, ''));
}
function getMaxLabel() {
var labels = [],
max = 0,
base = '';
$('.data-grid table tbody tr td:first-child').each(function(i, td) {
labels.push(trimLabel($(td).text()));
});
labels.forEach(function(label) {
var match = /([a-zA-z]+)-([0-9]+)/.exec(label),
n = +match[2];
base = match[1];
if ( n > max )
max = n;
});
return [base, max]
}
function getNextLabel() {
var baseMax = getMaxLabel();
return baseMax[0]+'-'+(+baseMax[1]+1);
}
function getNumOfSync() {
var checked = 0;
$('.data-grid table tbody td input:checkbox').each(function(index, cb) {
if ( $(cb).attr('checked') )
checked++;
});
return checked;
}
function validate_sync(event) {
var checkbox = $(event.target);
if ( checkbox.attr('checked') ) {
if ( getNumOfSync() > MAX_SYNC ) {
alert('No more than ' + MAX_SYNC + ' nodes can be in sync-mode!')
checkbox.attr('checked', false);
}
} else if ( checkbox.parents().eq(1).find('input:radio').attr('checked') ) {
alert('Primary node is always in sync-mode!');
checkbox.attr('checked', true);
}
}
var primary = $('.data-grid table tbody input:radio[checked="checked"]').parents().eq(1);
function validate_primary(event) {
var radio = $(event.target),
checkbox = radio.parents().eq(1).find('input:checkbox');
if ( !checkbox.attr('checked') ) {
if ( getNumOfSync() == MAX_SYNC )
primary.find('input:checkbox').attr('checked', false);
checkbox.attr('checked', true);
}
primary = radio.parents().eq(1);
}
$('.data-grid table tbody td input:checkbox').click(validate_sync);
$('.data-grid table tbody td input:radio').click(validate_primary);
$('button#node-add').click(function() {
debugger;
if ( $('.data-grid table tbody tr').length >= MAX_NODES ) {
alert('Maximum number of nodes ('+MAX_NODES+') already reached.');
return;
}
var lastRow = $('.data-grid table tbody tr:last-child'),
clone = lastRow.clone(),
nameCell = clone.find('td:first-child'),
hidden = nameCell.find('input:hidden'),
match = /([^@]+)@@([0-9]+)@@(.+)/.exec(hidden.attr('name')),
index = +match[2]+1,
nextLabel = getNextLabel();
nameCell.text(nextLabel);
hidden.val(nextLabel);
hidden.attr('name', match[1]+'@@'+index+'@@'+match[3]);
nameCell.append(hidden);
// toggle of sync and primary buttons of clone
clone.find('input:checkbox').attr('checked', false);
clone.find('input:checkbox').click(validate_sync);
clone.find('input:radio').attr('checked', false);
clone.find('input:radio').click(validate_primary);
lastRow.after(clone);
});
$('button#node-remove').click(function() {
if ( $('.data-grid table tbody tr').length <= MIN_NODES ) {
alert('There cannot be less than ' + MIN_NODES + ' nodes');
return;
}
var labelNum = getMaxLabel(),
label = labelNum.join('-'),
rowRef = '.data-grid table tbody tr:contains('+label+')';
if ( $(rowRef + ' :radio[checked="checked"]') ) {
label = labelNum[0] + '-' + (labelNum[1] - 1);
$('.data-grid table tbody tr:contains('+label+') :radio').attr(
'checked', 'checked');
}
$(rowRef).remove();
});
});
</script>
{% endif %}
<script>
$('.data-grid .table_caption').remove()
</script>

View File

@ -11,6 +11,5 @@ PyYAML>=3.1.0
oslo.log>=1.2.0 # Apache-2.0
# not listed in global requirements
django-floppyforms>=1.1
yaql>=0.2.6,<0.3 # Apache 2.0 License
python-muranoclient>=0.5.6