From ba27042d65c1bf47556cfecfbe492dab9dc88b3b Mon Sep 17 00:00:00 2001 From: Tihomir Trifonov Date: Mon, 23 Jul 2012 18:59:51 +0300 Subject: [PATCH] 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 in the (incl. the table_caption) than the 's in . Thus the custom parser needs to be added to [i-1] header, while the header itself needs [i], as the parser is processed in , 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 --- horizon/dashboards/nova/containers/tables.py | 7 ++- horizon/dashboards/nova/instances/tables.py | 4 +- horizon/dashboards/nova/volumes/tables.py | 4 +- horizon/dashboards/syspanel/flavors/tables.py | 10 +++- .../dashboards/syspanel/instances/tables.py | 3 +- horizon/static/horizon/js/horizon.tables.js | 51 +++++++++++++++---- horizon/usage/tables.py | 3 +- 7 files changed, 63 insertions(+), 19 deletions(-) diff --git a/horizon/dashboards/nova/containers/tables.py b/horizon/dashboards/nova/containers/tables.py index 6a1950622..f756e623e 100644 --- a/horizon/dashboards/nova/containers/tables.py +++ b/horizon/dashboards/nova/containers/tables.py @@ -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 diff --git a/horizon/dashboards/nova/instances/tables.py b/horizon/dashboards/nova/instances/tables.py index 116ce1540..f42d191ad 100644 --- a/horizon/dashboards/nova/instances/tables.py +++ b/horizon/dashboards/nova/instances/tables.py @@ -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), diff --git a/horizon/dashboards/nova/volumes/tables.py b/horizon/dashboards/nova/volumes/tables.py index 5d6e082d9..59239d02b 100644 --- a/horizon/dashboards/nova/volumes/tables.py +++ b/horizon/dashboards/nova/volumes/tables.py @@ -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"), diff --git a/horizon/dashboards/syspanel/flavors/tables.py b/horizon/dashboards/syspanel/flavors/tables.py index 3380725ad..d17e2d37c 100644 --- a/horizon/dashboards/syspanel/flavors/tables.py +++ b/horizon/dashboards/syspanel/flavors/tables.py @@ -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,) diff --git a/horizon/dashboards/syspanel/instances/tables.py b/horizon/dashboards/syspanel/instances/tables.py index 8933258d6..62f097f60 100644 --- a/horizon/dashboards/syspanel/instances/tables.py +++ b/horizon/dashboards/syspanel/instances/tables.py @@ -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"), diff --git a/horizon/static/horizon/js/horizon.tables.js b/horizon/static/horizon/js/horizon.tables.js index 9e1304fc0..3d6169f43 100644 --- a/horizon/static/horizon/js/horizon.tables.js +++ b/horizon/static/horizon/js/horizon.tables.js @@ -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 in + // than 's in + header_options[i-1] = {sorter: 'sizeSorter'}; + } + }); + $table.tablesorter({ + headers: header_options, + cancelSelection: false + }); + } }); }; diff --git a/horizon/usage/tables.py b/horizon/usage/tables.py index c05bdb66a..a679e1344 100644 --- a/horizon/usage/tables.py +++ b/horizon/usage/tables.py @@ -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),))