Added custom parser for tablesorter.js

A custom parser is needed for sorting of
custom type columns like the 'Size' columns.
The 'KB', 'MB', 'GB'... values break the sorter
logic and need to be parsed manually.

The custom parser works on all 'Size'/'RAM'/'Memory'
columns. It works also on flavor-info columns like:

    `m1.tiny | 512MB RAM | 1 VCPU | 0 Disk`

The custom parser sorts these lines based on the RAM value.

Note: as seen in horizon.tables.js file, an workaround was
added as there is one more <th> in the <thead> (incl. the table_caption)
than the <td>'s in <tbody>. Thus the custom parser needs
to be added to [i-1] header, while the header itself needs [i],
as the parser is processed in <tbody>, while headers are processed
including the table_caption... This might be a problem at some time,
although currently it works...

Fixes bug 1013475

Change-Id: I793ae330c33fde4e625f96e55fdda3748c84b8c3
This commit is contained in:
Tihomir Trifonov 2012-07-23 18:59:51 +03:00
parent 76246c6b18
commit ba27042d65
7 changed files with 63 additions and 19 deletions

View File

@ -92,7 +92,9 @@ class ContainersTable(tables.DataTable):
objects = tables.Column("object_count",
verbose_name=_('Objects'),
empty_value="0")
size = tables.Column(get_size_used, verbose_name=_('Size'))
size = tables.Column(get_size_used,
verbose_name=_('Size'),
attrs={'data-type': 'size'})
def get_object_id(self, container):
return container.name
@ -159,7 +161,8 @@ class ObjectsTable(tables.DataTable):
size = tables.Column("size",
verbose_name=_('Size'),
filters=(filesizeformat,),
summation="sum")
summation="sum",
attrs={'data-type': 'size'})
def get_object_id(self, obj):
return obj.name

View File

@ -268,7 +268,9 @@ class InstancesTable(tables.DataTable):
link=("horizon:nova:instances:detail"),
verbose_name=_("Instance Name"))
ip = tables.Column(get_ips, verbose_name=_("IP Address"))
size = tables.Column(get_size, verbose_name=_("Size"))
size = tables.Column(get_size,
verbose_name=_("Size"),
attrs={'data-type': 'size'})
keypair = tables.Column(get_keyname, verbose_name=_("Keypair"))
status = tables.Column("status",
filters=(title, replace_underscores),

View File

@ -137,7 +137,9 @@ class VolumesTableBase(tables.DataTable):
description = tables.Column("display_description",
verbose_name=_("Description"),
truncate=40)
size = tables.Column(get_size, verbose_name=_("Size"))
size = tables.Column(get_size,
verbose_name=_("Size"),
attrs={'data-type': 'size'})
status = tables.Column("status",
filters=(title,),
verbose_name=_("Status"),

View File

@ -24,11 +24,17 @@ class CreateFlavor(tables.LinkAction):
classes = ("ajax-modal", "btn-create")
def get_size(flavor):
return _("%sMB") % flavor.ram
class FlavorsTable(tables.DataTable):
flavor_id = tables.Column('id', verbose_name=('ID'))
name = tables.Column('name', verbose_name=_('Flavor Name'))
vcpus = tables.Column('vcpus', verbose_name=_('VCPUs'))
ram = tables.Column('ram', verbose_name=_('Memory'))
ram = tables.Column(get_size,
verbose_name=_('Memory'),
attrs={'data-type': 'size'})
disk = tables.Column('disk', verbose_name=_('Root Disk'))
ephemeral = tables.Column('OS-FLV-EXT-DATA:ephemeral',
verbose_name=_('Ephemeral Disk'))
@ -37,4 +43,4 @@ class FlavorsTable(tables.DataTable):
name = "flavors"
verbose_name = _("Flavors")
table_actions = (CreateFlavor, DeleteFlavor)
row_actions = (DeleteFlavor, )
row_actions = (DeleteFlavor,)

View File

@ -74,7 +74,8 @@ class SyspanelInstancesTable(tables.DataTable):
ip = tables.Column(get_ips, verbose_name=_("IP Address"))
size = tables.Column(get_size,
verbose_name=_("Size"),
classes=('nowrap-col',))
classes=('nowrap-col',),
attrs={'data-type': 'size'})
status = tables.Column("status",
filters=(title, replace_underscores),
verbose_name=_("Status"),

View File

@ -147,21 +147,50 @@ horizon.datatables.confirm = function (action) {
return modal;
};
$.tablesorter.addParser({
// set a unique id
id: 'sizeSorter',
is: function(s) {
// Not an auto-detected parser
return false;
},
// compare int values
format: function(s) {
var sizes = {BYTE: 0, B: 0, KB: 1, MB: 2,
GB: 3, TB: 4, PB: 5};
var regex = /([\d\.,]+)\s*(byte|B|KB|MB|GB|TB|PB)+/i;
var match = s.match(regex);
if (match && match.length === 3){
return parseFloat(match[1]) *
Math.pow(1024, sizes[match[2].toUpperCase()]);
}
return parseInt(s, 10);
},
type: 'numeric'
});
horizon.datatables.set_table_sorting = function (parent) {
// Function to initialize the tablesorter plugin strictly on sortable columns.
$(parent).find("table.table").each(function () {
var $this = $(this),
var $table = $(this),
header_options = {};
$this.find("thead th").each(function (i, val) {
// Disable if not sortable or has <= 1 item
if (!$(this).hasClass('sortable') || $this.find('tbody tr').not('.empty').length <= 1) {
header_options[i] = {sorter: false};
}
});
$this.tablesorter({
headers: header_options,
cancelSelection: false
});
// Disable if not sortable or has <= 1 item
if ($table.find('tbody tr').not('.empty').length > 1){
$table.find("thead th").each(function (i, val) {
$th = $(this);
if (!$th.hasClass('sortable')) {
header_options[i] = {sorter: false};
} else if ($th.data('type') == 'size'){
// set as [i-1] as there is one more <th> in <thead>
// than <td>'s in <tbody>
header_options[i-1] = {sorter: 'sizeSorter'};
}
});
$table.tablesorter({
headers: header_options,
cancelSelection: false
});
}
});
};

View File

@ -20,7 +20,8 @@ class BaseUsageTable(tables.DataTable):
disk = tables.Column('local_gb', verbose_name=_("Disk"))
memory = tables.Column('memory_mb',
verbose_name=_("RAM"),
filters=(mbformat,))
filters=(mbformat,),
attrs={"data-type": "size"})
hours = tables.Column('vcpu_hours', verbose_name=_("VCPU Hours"),
filters=(lambda v: floatformat(v, 2),))