Add utils for better column handling
In most OSC and plugin codes, we have column definitions for list operations but some better way of column definitions helps developers. This commit proposes some utility functions. We can define column definitions in more meaningful way. It also potentially helps us if we would like to show same field names in the list and show operations. At now, the list operation replaces API attribute names, but the show operation shows API attribute names without modification. A sample usage of these functions are found at: https://github.com/openstack/python-neutronclient/blob/master/neutronclient/osc/v2/fwaas/firewallgroup.py This commit moves osc_lib/utils.py to a subdirectory. The file is now a bit long and it would be nice if the module is split into meaningful pieces. Change-Id: Ife59ea35f5fad05173d3405718c9615ca97915fa
This commit is contained in:
parent
58a4d3cb6e
commit
75700a7b42
@ -24,6 +24,7 @@ from osc_lib import exceptions
|
||||
from osc_lib.tests import fakes
|
||||
from osc_lib.tests import utils as test_utils
|
||||
from osc_lib import utils
|
||||
from osc_lib.utils import columns as column_utils
|
||||
|
||||
PASSWORD = "Pa$$w0rd"
|
||||
WASSPORD = "Wa$$p0rd"
|
||||
@ -707,6 +708,49 @@ class TestFindResource(test_utils.TestCase):
|
||||
self.assertEqual(expected, actual_unsorted)
|
||||
|
||||
|
||||
class TestColumnUtils(test_utils.TestCase):
|
||||
|
||||
def test_get_column_definitions(self):
|
||||
attr_map = (
|
||||
('id', 'ID', column_utils.LIST_BOTH),
|
||||
('tenant_id', 'Project', column_utils.LIST_LONG_ONLY),
|
||||
('name', 'Name', column_utils.LIST_BOTH),
|
||||
('summary', 'Summary', column_utils.LIST_SHORT_ONLY),
|
||||
)
|
||||
headers, columns = column_utils.get_column_definitions(
|
||||
attr_map, long_listing=False)
|
||||
self.assertEqual(['id', 'name', 'summary'], columns)
|
||||
self.assertEqual(['ID', 'Name', 'Summary'], headers)
|
||||
|
||||
def test_get_column_definitions_long(self):
|
||||
attr_map = (
|
||||
('id', 'ID', column_utils.LIST_BOTH),
|
||||
('tenant_id', 'Project', column_utils.LIST_LONG_ONLY),
|
||||
('name', 'Name', column_utils.LIST_BOTH),
|
||||
('summary', 'Summary', column_utils.LIST_SHORT_ONLY),
|
||||
)
|
||||
headers, columns = column_utils.get_column_definitions(
|
||||
attr_map, long_listing=True)
|
||||
self.assertEqual(['id', 'tenant_id', 'name'], columns)
|
||||
self.assertEqual(['ID', 'Project', 'Name'], headers)
|
||||
|
||||
def test_get_columns(self):
|
||||
item = {
|
||||
'id': 'test-id',
|
||||
'tenant_id': 'test-tenant_id',
|
||||
# 'name' is not included
|
||||
'foo': 'bar', # unknown attribute
|
||||
}
|
||||
attr_map = (
|
||||
('id', 'ID', column_utils.LIST_BOTH),
|
||||
('tenant_id', 'Project', column_utils.LIST_LONG_ONLY),
|
||||
('name', 'Name', column_utils.LIST_BOTH),
|
||||
)
|
||||
columns, display_names = column_utils.get_columns(item, attr_map)
|
||||
self.assertEqual(tuple(['id', 'tenant_id', 'foo']), columns)
|
||||
self.assertEqual(tuple(['ID', 'Project', 'foo']), display_names)
|
||||
|
||||
|
||||
class TestAssertItemEqual(test_utils.TestCommand):
|
||||
|
||||
def test_assert_normal_item(self):
|
||||
|
99
osc_lib/utils/columns.py
Normal file
99
osc_lib/utils/columns.py
Normal file
@ -0,0 +1,99 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import operator
|
||||
|
||||
LIST_BOTH = 'both'
|
||||
LIST_SHORT_ONLY = 'short_only'
|
||||
LIST_LONG_ONLY = 'long_only'
|
||||
|
||||
|
||||
def get_column_definitions(attr_map, long_listing):
|
||||
"""Return table headers and column names for a listing table.
|
||||
|
||||
An attribute map (attr_map) is a list of table entry definitions
|
||||
and the format of the map is as follows:
|
||||
|
||||
:param attr_map: a list of table entry definitions.
|
||||
Each entry should be a tuple consisting of
|
||||
(API attribute name, header name, listing mode). For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
(('id', 'ID', LIST_BOTH),
|
||||
('name', 'Name', LIST_BOTH),
|
||||
('tenant_id', 'Project', LIST_LONG_ONLY))
|
||||
|
||||
The third field of each tuple must be one of LIST_BOTH,
|
||||
LIST_LONG_ONLY (a corresponding column is shown only in a long mode),
|
||||
or LIST_SHORT_ONLY (a corresponding column is shown only
|
||||
in a short mode).
|
||||
:param long_listing: A boolean value which indicates a long listing
|
||||
or not. In most cases, parsed_args.long is passed to this argument.
|
||||
:return: A tuple of a list of table headers and a list of column names.
|
||||
|
||||
"""
|
||||
|
||||
if long_listing:
|
||||
headers = [hdr for col, hdr, listing_mode in attr_map
|
||||
if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)]
|
||||
columns = [col for col, hdr, listing_mode in attr_map
|
||||
if listing_mode in (LIST_BOTH, LIST_LONG_ONLY)]
|
||||
else:
|
||||
headers = [hdr for col, hdr, listing_mode in attr_map if listing_mode
|
||||
if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)]
|
||||
columns = [col for col, hdr, listing_mode in attr_map if listing_mode
|
||||
if listing_mode in (LIST_BOTH, LIST_SHORT_ONLY)]
|
||||
|
||||
return headers, columns
|
||||
|
||||
|
||||
def get_columns(item, attr_map=None):
|
||||
"""Return pair of resource attributes and corresponding display names.
|
||||
|
||||
:param item: a dictionary which represents a resource.
|
||||
Keys of the dictionary are expected to be attributes of the resource.
|
||||
Values are not referred to by this method.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{'id': 'myid', 'name': 'myname',
|
||||
'foo': 'bar', 'tenant_id': 'mytenan'}
|
||||
|
||||
:param attr_map: a list of mapping from attribute to display name.
|
||||
The same format is used as for get_column_definitions attr_map.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
(('id', 'ID', LIST_BOTH),
|
||||
('name', 'Name', LIST_BOTH),
|
||||
('tenant_id', 'Project', LIST_LONG_ONLY))
|
||||
|
||||
:return: A pair of tuple of attributes and tuple of display names.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
(('id', 'name', 'tenant_id', 'foo'), # attributes
|
||||
('ID', 'Name', 'Project', 'foo') # display names
|
||||
|
||||
Both tuples of attributes and display names are sorted by display names
|
||||
in the alphabetical order.
|
||||
Attributes not found in a given attr_map are kept as-is.
|
||||
"""
|
||||
attr_map = attr_map or tuple([])
|
||||
_attr_map_dict = dict((col, hdr) for col, hdr, listing_mode in attr_map)
|
||||
|
||||
columns = [(column, _attr_map_dict.get(column, column))
|
||||
for column in item.keys()]
|
||||
columns = sorted(columns, key=operator.itemgetter(1))
|
||||
return (tuple(col[0] for col in columns),
|
||||
tuple(col[1] for col in columns))
|
Loading…
Reference in New Issue
Block a user