Add cmd line app

This commit is contained in:
Tom 2025-02-27 16:45:57 +00:00
parent 68ad80e435
commit 8306fb4c3e
3 changed files with 101 additions and 64 deletions

View File

@ -23,5 +23,7 @@ use `--input` and `--output` to specify input and output files respectively.
There's some handy test data in the `tests/data` directory. For example:
```bash
gzip -dc tests/data/fdb_list_compact.gz| qubed --from=fdblist
gzip -dc tests/data/fdb_list_compact.gz| qubed convert --from=fdb --to=text --output=qube.txt
gzip -dc tests/data/fdb_list_porcelain.gz| qubed convert --from=fdb --to=json --output=qube.json
gzip -dc tests/data/fdb_list_compact.gz | qubed convert --from=fdb --to=html --output=qube.html
```

View File

@ -18,6 +18,9 @@ requires-python = ">= 3.11"
dynamic = ["version"]
dependencies = [
"frozendict",
"rich",
"numpy",
"click",
]
# Because this is a mixed rust/python project the structure is src/python/qubed rather than the more typical src/qubed

View File

@ -1,82 +1,114 @@
import argparse
import sys
import time
import click
import psutil
from rich.console import Console
from rich.layout import Layout
from rich.live import Live
from rich.panel import Panel
from rich.spinner import Spinner
from rich.text import Text
from qubed import Qube
from qubed.convert import parse_fdb_list
console = Console(stderr=True)
process = psutil.Process()
PRINT_INTERVAL = 0.25
@click.group()
def main():
parser = argparse.ArgumentParser(
description="Generate a compressed tree from various inputs."
)
subparsers = parser.add_subparsers(title="subcommands", required=True)
parser_convert = subparsers.add_parser(
"convert", help="Convert trees from one format to another."
)
# parser_another = subparsers.add_parser(
# "another_subcommand", help="Does something else"
# )
parser_convert.add_argument(
"--input",
type=argparse.FileType("r"),
default=sys.stdin,
help="Specify the input file (default: standard input).",
)
parser_convert.add_argument(
"--output",
type=argparse.FileType("w"),
default=sys.stdout,
help="Specify the output file (default: standard output).",
)
parser_convert.add_argument(
"--input_format",
choices=["fdb", "mars"],
default="fdb",
help="""Specify the input format:
fdb: the output of fdb list --porcelain
mars: the output of mars list
""",
)
parser_convert.add_argument(
"--output_format",
choices=["text", "html"],
default="text",
help="Specify the output format (text or html).",
)
parser_convert.set_defaults(func=convert)
args = parser.parse_args()
args.func(args)
"""Command-line tool for working with trees."""
pass
def convert(args):
@main.command()
@click.option(
"--input",
type=click.File("r"),
default="-",
help="Specify the input file (default: standard input).",
)
@click.option(
"--output",
type=click.File("w"),
default="-",
help="Specify the output file (default: standard output).",
)
@click.option(
"--from",
"from_format",
type=click.Choice(["fdb", "mars"]),
default="fdb",
help="Specify the input format: fdb (fdb list --porcelain) or mars (mars list).",
)
@click.option(
"--to",
"to_format",
type=click.Choice(["text", "html", "json"]),
default="text",
help="Specify the output format: text, html, json.",
)
def convert(input, output, from_format, to_format):
"""Convert trees from one format to another."""
q = Qube.empty()
for datacube in parse_fdb_list(args.input):
new_branch = Qube.from_datacube(datacube)
q = q | Qube.from_datacube(datacube)
t = time.time()
i0 = 0
n0 = 0
depth = 5
log = Text()
summary = Layout()
summary.split_column(
Layout(name="upper"),
Layout(name="qube"),
)
summary["upper"].split_row(
Layout(name="performance"),
Layout(log, name="log"),
)
spinner = Spinner("aesthetic", text="Performance", speed=0.3)
# output = match args.output_format:
# case "text":
# str(q)
# case "html":
# q.html()
output = "fw"
with Live(summary, auto_refresh=False, transient=True, console=console) as live:
for i, datacube in enumerate(parse_fdb_list(input)):
new_branch = Qube.from_datacube(datacube)
q = q | new_branch
with open(args.output, "w") as f:
f.write(output)
if time.time() - t > PRINT_INTERVAL:
tree = q.__str__(depth=depth)
if tree.count("\n") > 20:
depth -= 1
if tree.count("\n") < 5:
depth += 1
console.print([1, 2, 3])
console.print("[blue underline]Looks like a link")
console.print(locals())
console.print("FOO", style="white on blue")
summary["performance"].update(
Panel(
Text.assemble(
f"The Qube has {q.n_leaves} leaves and {q.n_nodes} internal nodes so far.\n",
f"{(i - i0) / (time.time() - t) / PRINT_INTERVAL:.0f} lines per second. ",
f"{(q.n_leaves - n0) / (time.time() - t):.0f} leaves per second.\n",
f"Memory usage: {process.memory_info().rss / 1024 / 1024:.0f} MB\n",
),
title=spinner.render(time.time()),
border_style="blue",
)
)
summary["qube"].update(
Panel(tree, title=f"Qube (depth {depth})", border_style="blue")
)
summary["log"].update(
Panel(
f"{datacube}", border_style="blue", title="Last Datacube Added"
)
)
live.refresh()
i0 = i
n0 = q.n_leaves
t = time.time()
output_content = str(q) if to_format == "text" else q.html().html
output.write(output_content)
if __name__ == "__main__":