Skip to content

Commit f5d756b

Browse files
author
René Moser
authored
Implement natural sort for table output (#28)
1 parent aa85ed6 commit f5d756b

8 files changed

Lines changed: 45 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
77

88
- cloudscale-sdk updated to 0.5.0.
99
- Add option `--detach` for volumes to detach.
10+
- Add natural sort for table output.
1011

1112
## v1.0.1 (2020-10-09)
1213

cloudscale_cli/commands/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
import time
33
import click
44
import jmespath
5+
from natsort import natsorted, ns
56
from cloudscale import Cloudscale, CloudscaleApiException
6-
from ..util import to_table, to_pretty_json, tags_to_dict
7+
from ..util import to_table, to_pretty_json, tags_to_dict, format_json
78

89
if sys.stdout.isatty():
910
from yaspin import yaspin as Spinner
@@ -38,6 +39,9 @@ def __init__(self, cloud_resource_name=None, api_token=None, profile=None, debug
3839
# Alternate key to look for the resource as 'name'
3940
self.resource_name_key = 'name'
4041

42+
# Usually we want to sort the table by the key used as name
43+
self.resource_table_sort_key = None
44+
4145
self.response_transform_json = None
4246

4347
def get_client_resource(self):
@@ -49,7 +53,16 @@ def _format_output(self, response):
4953
else:
5054
if isinstance(response, dict):
5155
response = [response]
52-
return to_table(response, self.headers, self.response_transform_json)
56+
57+
if response:
58+
response = format_json(response, self.response_transform_json)
59+
sort_by_key = self.resource_table_sort_key or self.resource_name_key
60+
if sort_by_key:
61+
if 'zone' in self.headers:
62+
response = natsorted(response, key=lambda x: (x['zone'], x[sort_by_key]), alg=ns.IGNORECASE)
63+
else:
64+
response = natsorted(response, key=lambda x: x[sort_by_key], alg=ns.IGNORECASE)
65+
return to_table(response, self.headers)
5366

5467
def cmd_list(self, filter_tag=None, filter_json=None, action=None, delete=False, force=False, wait=False):
5568
if action and delete:

cloudscale_cli/commands/region.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def region(ctx):
88
'slug',
99
'zones',
1010
]
11+
ctx.obj.resource_table_sort_key = 'slug'
1112

1213
@click.option('--filter-json')
1314
@region.command("list")

cloudscale_cli/commands/server_group.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ def server_group(ctx):
1212
'tags',
1313
'uuid',
1414
]
15+
ctx.obj.response_transform_json = '''
16+
[].{
17+
"name": name,
18+
"type": type,
19+
"servers": servers,
20+
"zone": zone.slug,
21+
"tags": tags,
22+
"uuid": uuid
23+
}
24+
'''
1525

1626
@click.option('--filter-tag')
1727
@click.option('--filter-json')

cloudscale_cli/commands/subnet.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def subnet(ctx):
1313
'tags',
1414
]
1515
ctx.obj.resource_name_key = None
16+
ctx.obj.resource_table_sort_key = 'cidr'
1617

1718
@click.option('--filter-tag')
1819
@click.option('--filter-json')

cloudscale_cli/commands/volume.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ def volume(ctx):
1414
'server_uuids',
1515
'uuid',
1616
]
17+
ctx.obj.response_transform_json = '''
18+
[].{
19+
"name": name,
20+
"type": type,
21+
"tags": tags,
22+
"server_uuids": server_uuids,
23+
"zone": zone.slug,
24+
"uuid": uuid
25+
}
26+
'''
1727

1828
@click.option('--filter-tag')
1929
@click.option('--filter-json')

cloudscale_cli/util.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import json
2+
import jmespath
23
from click import Group
34
from collections import OrderedDict
45
from tabulate import tabulate
56
from pygments import highlight, lexers, formatters
6-
import jmespath
77

88

99
class OrderedGroup(Group):
@@ -19,14 +19,16 @@ def list_commands(self, ctx):
1919
return sorted(self.commands.keys())
2020

2121

22+
def format_json(data: list, format_json: str = None) -> list:
23+
if format_json:
24+
data = jmespath.search(format_json, data)
25+
return data
26+
2227
def to_table(data: list, headers: list, format_json: str = None) -> str:
2328
'''
2429
Turn a list into a table
2530
'''
2631

27-
if format_json:
28-
data = jmespath.search(format_json, data)
29-
3032
formated_headers = []
3133

3234
cols = list()

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ tabulate
44
pygments
55
jmespath
66
yaspin
7+
natsort

0 commit comments

Comments
 (0)