Skip to content

Commit

Permalink
CLI (#174)
Browse files Browse the repository at this point in the history
* CLI
* Tune.monotonic
* Instrument.print_tree
* print_history
  • Loading branch information
ddkohler authored Jan 25, 2024
1 parent d038b19 commit 964756f
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

### Added
- command line interface for simple queries of instruments
- tunes now have a `monotonic` property
- `Instrument.print_tree`
- `print_history` function

### Fixed
- Inserted WrightTools import into _map and _offset

Expand Down
2 changes: 2 additions & 0 deletions attune/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@
from ._tune_test import *
from ._update_merge import *
from .io import *

from . import _cli
7 changes: 4 additions & 3 deletions attune/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
# --- import --------------------------------------------------------------------------------------


import pathlib
import os


# ---- define -------------------------------------------------------------------------------------


here = os.path.abspath(os.path.dirname(__file__))
here = pathlib.Path(__file__).resolve().parent


__all__ = ["__version__", "__branch__"]
Expand All @@ -20,12 +21,12 @@


# read from VERSION file
with open(os.path.join(here, "VERSION")) as f:
with open(here / "VERSION") as f:
__version__ = f.read().strip()


# add git branch, if appropriate
p = os.path.join(os.path.dirname(here), ".git", "HEAD")
p = here.parent / ".git" / "HEAD"
if os.path.isfile(p):
with open(p) as f:
__branch__ = f.readline().rstrip().split(r"/")[-1]
Expand Down
9 changes: 9 additions & 0 deletions attune/_arrangement.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ def __init__(self, name: str, tunes: Dict[str, Union[DiscreteTune, Tune, dict]])
}
self._ind_units: str = "nm"

def _print_tunes(self, prefix):
for i, (name, tune) in enumerate(self.tunes.items()):
if i + 1 == len(self.tunes):
b = "└── "
else:
b = "├── "
s = prefix + b + "{0}: {1}".format(name, tune._leaf)
print(s)

def __repr__(self):
return f"Arrangement({repr(self.name)}, {repr(self.tunes)})"

Expand Down
43 changes: 43 additions & 0 deletions attune/_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import click

from .__version__ import __version__
from . import _store as store


@click.group()
@click.version_option(__version__)
def main():
pass


@main.command(name="inspect", help="show the structure of an instrument")
@click.argument("instrument", nargs=1)
def inspect(instrument, arrangement=None, tune=None):
instr = store.load(instrument)
instr.print_tree()


@main.command(name="catalog", help="lists the instrument names in the catalog")
def catalog():
for ins in store.catalog():
print(ins)


@main.command(name="history", help="show the change history of an instrument")
@click.argument("instrument", nargs=1)
@click.option("-n", default=10, help="number of records to list (default is 10)")
@click.option("--start", "-s", default="now", help="date to start history (default is now)")
@click.option(
"--forward",
"-f",
is_flag=True,
show_default=True,
default=False,
help="when specified, history will search forwards in time",
)
def history(instrument, n=10, start="now", forward=False):
store.print_history(instrument, n, start, reverse=not forward)


if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions attune/_instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,23 @@ def __call__(self, ind_value, arrangement_name=None) -> Note:
)
return note

def print_tree(self):
"""Print a ascii-formatted tree representation of the instrument contents."""
print("{0}".format(self.name))
self._print_arrangements("")

def _print_arrangements(self, prefix):
for i, (name, arrangement) in enumerate(self.arrangements.items()):
if i + 1 == len(self.arrangements):
b = "└── "
add_prefix = " "
else:
b = "├── "
add_prefix = "│ "
s = prefix + b + "{0}".format(name)
print(s)
arrangement._print_tunes(prefix + add_prefix)

def __getitem__(self, item):
return self._arrangements[item]

Expand Down
60 changes: 59 additions & 1 deletion attune/_store.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tools to interact with the attune store."""

__all__ = ["catalog", "load", "restore", "store", "undo"]
__all__ = ["catalog", "load", "restore", "store", "undo", "print_history", "WalkHistory"]


from datetime import datetime, timedelta, timezone
Expand Down Expand Up @@ -137,6 +137,64 @@ def restore(name, time, reverse=True):
_store_instr(instr)


class WalkHistory:
"""iterator for a instrument's history"""

def __init__(self, name, start="now", reverse=True):
self.name = name
self.time = start
self.reverse = reverse
self.direction = -1 if reverse else 1

def __iter__(self):
return self

def __next__(self):
try:
self.current = load(self.name, self.time, self.reverse)
except ValueError:
raise StopIteration
self.time = self.current.load + self.direction * timedelta(milliseconds=1)
return self.current


def print_history(name, n=10, start="now", reverse: bool = True):
"""
Print the store's history of an instrument
Parameters
----------
name: str
Name of the instrument. The instrument name must be in the catalog
n: int
Number of change records to look up. Default is 10.
start: datetime.Datetime or str
Date and time from which the records begin listing. Default is "now".
reverse: bool
When false, history will search records forwards in time.
Returns
-------
history: str
a neatly formatted, multiline string overviewing the history of instrument changes
"""
title_string = f"{name}, going {'backwards' if reverse else 'forwards'}"
print(title_string + "-" * (80 - len(name)))
for i, inst in enumerate(WalkHistory(name, start, reverse)):
transition_type = None if inst.transition is None else inst.transition.type
print(
"{0:6} {1}{2} at {3}".format(
-i if reverse else i,
transition_type,
"." * (20 - len(transition_type)),
str(inst.load),
)
)
if i == n:
break
print("<end of history>")


def store(instrument, warn=True):
"""Store an instrument into the catalog.
Expand Down
21 changes: 21 additions & 0 deletions attune/_tune.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ def __init__(self, independent, dependent, *, dep_units=None, **kwargs):
self._dep_units = dep_units
self._interp = scipy.interpolate.interp1d(independent, dependent, fill_value="extrapolate")

@property
def _leaf(self):
out = " {0} points, ".format(self.independent.size)
functional_notation = "[{0}, {1}] {2} -> [{3}, {4}] {5}"
out += functional_notation.format(
self.ind_min,
self.ind_max,
self.ind_units,
self.dependent.min(),
self.dependent.max(),
self.dep_units,
)
out += ", {0}monotonic".format("" if self.monotonic else "non-")
return out

def __repr__(self):
if self.dep_units is None:
return f"Tune({repr(self.independent)}, {repr(self.dependent)})"
Expand Down Expand Up @@ -96,3 +111,9 @@ def ind_units(self):
def dep_units(self):
"""The units of the dependent (output) values."""
return self._dep_units

@property
def monotonic(self) -> bool:
"""Whether or not the dependent variable moves monotonically."""
checks = np.gradient(self.dependent) <= 0
return checks.all() or (not checks.any())
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def read(fname):
"appdirs",
"python-dateutil",
"maya",
"click",
],
extras_require={
"dev": ["black", "pre-commit", "pytest", "pytest-cov"],
Expand All @@ -61,4 +62,5 @@ def read(fname):
"Programming Language :: Python :: 3.10",
"Topic :: Scientific/Engineering",
],
entry_points={"console_scripts": ["attune = attune._cli:main"]},
)
5 changes: 4 additions & 1 deletion tests/workup/holistic/test_holistic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
import WrightTools as wt
import numpy as np
import pathlib
import sys

import pytest

__here__ = pathlib.Path(__file__).parent


@pytest.mark.xfail(sys.version_info > (3, 8), reason="math slightly changes after py3.8")
def test_single_channel():
# collect
d = wt.open(__here__ / "data.wt5")
Expand All @@ -25,13 +27,14 @@ def test_single_channel():
gtol=0.05,
level=True,
autosave=False,
save_directory=__here__ / "out",
save_directory=__here__,
)
d.close()
# check
assert reference == new


@pytest.mark.xfail(sys.version_info > (3, 8), reason="math slightly changes after py3.8")
def test_multiple_channels():
# collect
d = wt.open(__here__ / "data.wt5")
Expand Down

0 comments on commit 964756f

Please sign in to comment.