Browse Source

Truncate URLs by default to fit the terminal width

master
JustAnotherArchivist 4 years ago
parent
commit
9763370976
1 changed files with 37 additions and 3 deletions
  1. +37
    -3
      archivebot-jobs

+ 37
- 3
archivebot-jobs View File

@@ -1,8 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import datetime import datetime
import itertools
import json import json
import math import math
import os
import re import re
import sys import sys
import time import time
@@ -11,7 +13,7 @@ import urllib.request
# Column definitions # Column definitions
columns = { columns = {
'jobid': (lambda job, pipelines: job["job_data"]["ident"], ()), 'jobid': (lambda job, pipelines: job["job_data"]["ident"], ()),
'url': (lambda job, pipelines: job["job_data"]["url"], ()),
'url': (lambda job, pipelines: job["job_data"]["url"], ('truncatable',)),
'user': (lambda job, pipelines: job["job_data"]["started_by"], ()), 'user': (lambda job, pipelines: job["job_data"]["started_by"], ()),
'pipenick': (lambda job, pipelines: pipelines[job["job_data"]["pipeline_id"]] if job["job_data"]["pipeline_id"] in pipelines else "unknown", ()), 'pipenick': (lambda job, pipelines: pipelines[job["job_data"]["pipeline_id"]] if job["job_data"]["pipeline_id"] in pipelines else "unknown", ()),
'queued': (lambda job, pipelines: job["job_data"]["queued_at"], ('date',)), 'queued': (lambda job, pipelines: job["job_data"]["queued_at"], ('date',)),
@@ -27,6 +29,11 @@ columns = {
} }
defaultSort = 'jobid' defaultSort = 'jobid'


# Validate
if any('truncatable' in colDef[1] and any(x in colDef[1] for x in ('date', 'coloured', 'size')) for colDef in columns.values()):
# Truncation code can't handle renderers
raise RuntimeError('Invalid column definitions: cannot combine date/coloured/size with truncatable')

# Parse arguments # Parse arguments
class FilterAction(argparse.Action): class FilterAction(argparse.Action):
def __call__(self, parser, namespace, values, optionString = None): def __call__(self, parser, namespace, values, optionString = None):
@@ -78,6 +85,7 @@ parser.add_argument('--mode', choices = ('table', 'dashboard-regex', 'con-d-comm
])) ]))
parser.add_argument('--no-colours', '--no-colors', action = 'store_true', help = "Don't colourise the last activity column if it's been a while. (Table mode only)") parser.add_argument('--no-colours', '--no-colors', action = 'store_true', help = "Don't colourise the last activity column if it's been a while. (Table mode only)")
parser.add_argument('--no-table', action = 'store_true', help = 'Raw output without feeding through column(1); columns are separated by tabs. (Table mode only)') parser.add_argument('--no-table', action = 'store_true', help = 'Raw output without feeding through column(1); columns are separated by tabs. (Table mode only)')
parser.add_argument('--no-truncate', action = 'store_true', help = 'Disable truncating long values if the terminal width would be exceeded. (Table mode without --no-table only)')
parser.add_argument('--dates', action = 'store_true', help = 'Print dates instead of elapsed times for queued/started/last active columns. (Table mode only)') parser.add_argument('--dates', action = 'store_true', help = 'Print dates instead of elapsed times for queued/started/last active columns. (Table mode only)')
parser.add_argument('--format', help = 'Output format for the format mode; this must be a Python format string and can use any column name in lower-case with spaces replaced by underscores; e.g. "{url} {last_active}". (Format mode only)') parser.add_argument('--format', help = 'Output format for the format mode; this must be a Python format string and can use any column name in lower-case with spaces replaced by underscores; e.g. "{url} {last_active}". (Format mode only)')
args = parser.parse_args() args = parser.parse_args()
@@ -198,12 +206,38 @@ for column, (_, columnAttr) in columns.items():
elif isinstance(jobs[0][column], (int, float)): elif isinstance(jobs[0][column], (int, float)):
renderers[column] = str renderers[column] = str


for job in jobs:
for column in renderers:
job[column] = renderers[column](job[column])

# Truncate if applicable
printableColumns = {column: colDef for column, colDef in columns.items() if 'hidden' not in colDef[1]}
if not args.no_table and not args.no_truncate:
widthsD = {column: max(itertools.chain((len(column),), (len(job[column]) if isinstance(job[column], str) else len(job[column][1]) for job in jobs))) for column in printableColumns}
minWidthsD = {column: len(column) for column in printableColumns}
termWidth = os.get_terminal_size().columns
overage = sum(x + 2 for x in widthsD.values()) - 2 - termWidth
if overage > 0:
if sum((widthsD[column] if 'truncatable' not in colDef[1] else minWidthsD[column]) + 2 for column, colDef in printableColumns.items()) - 2 > termWidth:
# Even truncating all truncatable columns to the minimum width is not sufficient, i.e. can't match this terminal width. Print a warning and proceed normally
print('Sorry, cannot truncate columns to terminal width', file = sys.stderr)
else:
# Distribute overage to truncatable columns proportionally to each column's length over the minimum
truncatableColumns = {column: colDef for column, colDef in columns.items() if 'truncatable' in colDef[1]}
totalOverMin = sum(widthsD[column] - minWidthsD[column] for column in truncatableColumns)
trWidthsD = {column: math.floor(widthsD[column] - (widthsD[column] - minWidthsD[column]) / totalOverMin * overage) for column in truncatableColumns}
if sum(widthsD[column] - trWidthsD[column] for column in truncatableColumns) - overage == 1:
# Truncated one more character than necessary due to the flooring; add it again to the shortest column
trWidthsD[min(trWidthsD, key = trWidthsD.get)] += 1
for job in jobs:
for column in truncatableColumns:
if len(job[column]) > trWidthsD[column]:
job[column] = job[column][:trWidthsD[column] - 1] + '…'

# Print # Print
output = [] output = []
output.append(tuple(column.upper() for column in columns if "hidden" not in columns[column][1])) output.append(tuple(column.upper() for column in columns if "hidden" not in columns[column][1]))
for job in jobs: for job in jobs:
for column in renderers:
job[column] = renderers[column](job[column])
output.append(tuple(job[column] for column in columns if "hidden" not in columns[column][1])) output.append(tuple(job[column] for column in columns if "hidden" not in columns[column][1]))


if not args.no_table: if not args.no_table:


Loading…
Cancel
Save