updates+uptimes: Minor UI tweaks, less hacky sort.

Signed-off-by: James Antill <james@and.org>
This commit is contained in:
James Antill
2026-02-10 17:21:18 -05:00
parent a0cab4f3cc
commit 0633cda299

View File

@@ -50,9 +50,6 @@ conf_term_keyword = 'underline'
conf_term_time = 'underline' conf_term_time = 'underline'
conf_term_title = 'italic' conf_term_title = 'italic'
# Use version cmp() for hostnames.
conf_verscmp = True
# Use _ instead of , for number seperator. # Use _ instead of , for number seperator.
conf_num_sep_ = False conf_num_sep_ = False
@@ -152,7 +149,7 @@ def _user_conf_line(line):
key = 'conf_' + key.strip().lower() key = 'conf_' + key.strip().lower()
if key not in globals(): if key not in globals():
print(" Error: Configuration not found: ", key, file=sys.stderr) print(" Warn: Configuration not found: ", key, file=sys.stderr)
return return
if False: pass if False: pass
@@ -189,42 +186,85 @@ def _user_conf_line(line):
print(" Error: Configuration ", key,'bad op', file=sys.stderr) print(" Error: Configuration ", key,'bad op', file=sys.stderr)
return return
# This isn't fast but it's SMALL:
# Sort as: ABC1, ABC2, ABC10b, ... # This is kind of fast and kind of small. No re, and no allocation.
def verscmp(x, y): # Sort as: 0, 00, 000, 01, 011, 1, 11, a01, a1, z01, z1, etc.
if not conf_verscmp: def natcmp(x, y):
if x == y: """ Natural sort string comparison.
return 0 https://en.wikipedia.org/wiki/Natural_sort_order
if x > y: Aka. vercmp() """
def _cmp_xy_mix(): # One is a digit, the other isn't.
if inum is not None: # 0/1 vs. x/.
return 1 return 1
if x[i] > y[i]:
return 1
else:
return -1 return -1
xc = re.split(r'(\d+|\W+)', x) inum = None
yc = re.split(r'(\d+|\W+)', y) check_zeros = False
while xc and yc: for i in range(min(len(x), len(y))):
if xc[0] == yc[0]: if x[i] in "0123456789" and y[i] not in "0123456789":
xc.pop(0) return _cmp_xy_mix()
yc.pop(0) if x[i] not in "0123456789" and y[i] in "0123456789":
return _cmp_xy_mix()
if x[i] in "0123456789": # Both are digits...
if inum is None:
check_zeros = True
inum = 0
if check_zeros: # Leading zeros... (0 < 00 < 01 < 011 < 1 < 11)
if x[i] == '0' and y[i] == '0':
continue
elif x[i] == '0':
return -1
elif y[i] == '0':
return 1
else:
check_zeros = False
# If we are already in a number, we only care about the length or
# the first digit that is different.
if inum != 0:
continue continue
if xc[0].isnumeric(): if x[i] == y[i]:
if not yc[0].isnumeric(): continue
# Non-zero first digit, Eg. 7 < 9
inum = int(x[i]) - int(y[i])
continue
# Both are not digits...
if inum is not None and inum != 0:
return inum
inum = None
# Can be equal
if x[i] > y[i]:
return 1 return 1
nx = int(xc[0]) if x[i] < y[i]:
ny = int(yc[0])
if nx != ny: # don't make 0 == 00 ... we aren't rpm.
return nx - ny
elif yc[0].isnumeric():
return -1 return -1
# Neither numeric, but also 0 == 00 BS if len(x) > len(y):
if xc[0] > yc[0]: if inum is not None and inum != 0 and x[i+1] not in "0123456789":
return inum
return 1 return 1
if len(x) < len(y):
if inum is not None and inum != 0 and y[i+1] not in "0123456789":
return inum
return -1 return -1
return len(xc) - len(yc)
if inum is None: # Same length, not in a num.
assert x == y
return 0 # So the strings are equal.
return inum
class VerscmpString(): class NatCmp():
__slots__ = ['s',] __slots__ = ['s',]
def __init__(self, s): def __init__(self, s):
self.s = s self.s = s
@@ -236,16 +276,14 @@ class VerscmpString():
return self.s == other.s return self.s == other.s
def __gt__(self, other): def __gt__(self, other):
ret = verscmp(self.s, other.s) ret = natcmp(self.s, other.s)
if ret > 0: if ret > 0:
return True return True
if False and ret < 0:
return False
return False return False
# Given a list of strings, sort them using verscmp() # Given a list of strings, sort them using natcmp()
def _verscmp_strings(xs): def nat_sorted(xs):
for ret in sorted(VerscmpString(x) for x in xs): for ret in sorted(NatCmp(x) for x in xs):
yield ret.s yield ret.s
@@ -490,7 +528,7 @@ class Host():
return True return True
def __gt__(self, other): def __gt__(self, other):
ret = verscmp(self.name, other.name) ret = natcmp(self.name, other.name)
if ret > 0: if ret > 0:
return True return True
if ret < 0: if ret < 0:
@@ -1337,7 +1375,7 @@ def _cmd_stats(args):
max_nhosts_lvl_2 = 0 max_nhosts_lvl_2 = 0
max_update_lvl_2 = 0 max_update_lvl_2 = 0
max_uptime_lvl_2 = 0 max_uptime_lvl_2 = 0
for osi in _verscmp_strings(osdata['hosts']): for osi in nat_sorted(osdata['hosts']):
if '/' not in osi: if '/' not in osi:
max_nhosts_lvl_1 = max(max_nhosts_lvl_1, osdata['hosts'][osi]) max_nhosts_lvl_1 = max(max_nhosts_lvl_1, osdata['hosts'][osi])
supd = osdata['updates'][osi] / osdata['hosts'][osi] supd = osdata['updates'][osi] / osdata['hosts'][osi]
@@ -1385,7 +1423,7 @@ def _cmd_stats(args):
suf = _ui_t_high(suf) suf = _ui_t_high(suf)
return uiosi, uinhosts, uiupdates, uiuptimes, uinpdates, uinptimes, suf return uiosi, uinhosts, uiupdates, uiuptimes, uinpdates, uinptimes, suf
for osi in _verscmp_strings(osdata['hosts']): for osi in nat_sorted(osdata['hosts']):
if '/' not in osi: if '/' not in osi:
if len(osdata['vers'][osi]) == 1: if len(osdata['vers'][osi]) == 1:
subprefix = '' subprefix = ''
@@ -2003,7 +2041,7 @@ utf8: {conf_utf8}
{_conf_utf8_diff_os} = Different OS information, but machine id is the same {_conf_utf8_diff_os} = Different OS information, but machine id is the same
{_conf_utf8_diff_hw} = Machine id is different {_conf_utf8_diff_hw} = Machine id is different
{_hlp_als("host")} {_hlp_als("hosts")}
Eg. {prog} {args.hcmd} Eg. {prog} {args.hcmd}
{prog} {args.hcmd} 'batcave*' {prog} {args.hcmd} 'batcave*'
{prog} {args.hcmd} 'batcave*' 'noc*' {prog} {args.hcmd} 'batcave*' 'noc*'