Skip to content

feat: implement interactive shell and major documentation update for v2#226

Merged
smlloyd merged 1 commit intomasterfrom
feat-shell
Mar 6, 2026
Merged

feat: implement interactive shell and major documentation update for v2#226
smlloyd merged 1 commit intomasterfrom
feat-shell

Conversation

@smlloyd
Copy link
Copy Markdown
Member

@smlloyd smlloyd commented Mar 6, 2026

  • Implement 'aimbat shell' providing an interactive environment for project management
  • Rewrite README.md to emphasize v2 goals, features, and modern tech stack
  • Update documentation navigation and add comprehensive API usage snippets
  • Refine CLI and TUI logic for default event handling
  • Add functional tests for shell and TUI interfaces
  • Update dependencies (adding pytest-asyncio) and improve test assertions

📚 Documentation preview 📚: https://aimbat--226.org.readthedocs.build/en/226/

Copilot AI review requested due to automatic review settings March 6, 2026 04:00
import asyncio

import aimbat.db
import aimbat._tui.app
Comment thread docs/snippets/api_deduplicate.py Fixed
print(f"{event.time} {len(event.seismograms)} seismograms")

# Filter — seismograms marked as selected
selected = session.exec(
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the aimbat shell interactive command (a REPL with tab-completion, command history, and live ICCS status), refactors the get_default_event() API to return None instead of raising on empty DB, improves ICCS caching (process-level cache + read-only array flag), adds ICCS validation before parameter writes, and substantially rewrites the documentation (README, API docs, shell docs, and doc snippets). Functional tests for both the shell and the TUI are also added.

Changes:

  • get_default_event() signature changed to AimbatEvent | None; all call sites updated with assert guards or None checks
  • New aimbat shell REPL with tab-completion, event-context switching, and ICCS caching
  • Documentation rewrite (README, docs/usage/api.md, docs/usage/shell.md, docs/usage/cli.md) with new API snippet files

Reviewed changes

Copilot reviewed 33 out of 34 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/aimbat/core/_default_event.py Changed get_default_event() to return None instead of raising; resolve_event() now raises NoResultFound when no default exists
src/aimbat/core/_iccs.py Added process-level ICCS cache, clear_iccs_cache(), validate_iccs_construction(); arrays marked read-only; removed clone_to_mini
src/aimbat/io/_base.py Cache now returns read-only NumPy arrays
src/aimbat/core/_event.py set_event_parameter() gains validate_iccs keyword arg
src/aimbat/_cli/shell.py New interactive REPL shell command
src/aimbat/_cli/common.py Removed CliHints/HINTS
src/aimbat/_tui/app.py Updated to use resolve_event(), simplified ICCS lifecycle
src/aimbat/_tui/modals.py Updated for get_default_event() returning None
tests/functional/test_shell.py New functional tests for the shell REPL
tests/functional/test_tui.py New functional TUI tests via headless Textual pilot
tests/integration/core/test_*.py Added assert default_event is not None guards
tests/unit/_cli/test_common.py Removed TestCliHints class
pyproject.toml / uv.lock Added pytest-asyncio, updated ruff, platformdirs
README.md / docs/ Major documentation rewrite
CHANGELOG.md New entries for this release
zensical.toml Updated navigation labels
src/aimbat/app.py Registered shell subcommand

Comment thread src/aimbat/core/_iccs.py Outdated
Comment on lines +134 to +145
def validate_iccs_construction(session: Session, event: AimbatEvent) -> None:
"""Try to construct an ICCS instance for the event without caching the result.

Use this to check whether the event's current (possibly uncommitted) parameters
are compatible with ICCS construction before persisting them to the database.

Args:
session: Database session.
event: AimbatEvent.

Raises:
Any exception raised by ICCS construction (e.g. invalid parameter values).
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validate_iccs_construction function accepts a session: Session parameter but does not use it at all — all work is done with event.seismograms and event.parameters that were already loaded. The unused parameter adds noise to the public API and may confuse callers. Since the function is exported in __all__ it forms part of the public API. Consider either removing the session parameter or making the seismogram lazy-loading explicit so the parameter is genuinely needed.

Suggested change
def validate_iccs_construction(session: Session, event: AimbatEvent) -> None:
"""Try to construct an ICCS instance for the event without caching the result.
Use this to check whether the event's current (possibly uncommitted) parameters
are compatible with ICCS construction before persisting them to the database.
Args:
session: Database session.
event: AimbatEvent.
Raises:
Any exception raised by ICCS construction (e.g. invalid parameter values).
def validate_iccs_construction(_session: Session, event: AimbatEvent) -> None:
"""Try to construct an ICCS instance for the event without caching the result.
Use this to check whether the event's current (possibly uncommitted) parameters
are compatible with ICCS construction before persisting them to the database.
Args:
event: AimbatEvent to validate for ICCS construction.
Raises:
Any exception raised by ICCS construction (e.g. invalid parameter values).
Notes:
The ``_session`` argument is accepted for API compatibility and is not used.

Copilot uses AI. Check for mistakes.
Comment thread src/aimbat/core/_iccs.py Outdated
Comment on lines +148 to +170
seismograms = [
MiniICCSSeismogram(
begin_time=seis.begin_time,
delta=seis.delta,
data=seis.data,
t0=seis.t0,
t1=seis.t1,
flip=seis.flip,
select=seis.select,
extra={"id": seis.id},
)
for seis in event.seismograms
]
ICCS(
seismograms=seismograms,
window_pre=p.window_pre,
window_post=p.window_post,
bandpass_apply=p.bandpass_apply,
bandpass_fmin=p.bandpass_fmin,
bandpass_fmax=p.bandpass_fmax,
min_ccnorm=p.min_ccnorm,
context_width=settings.context_width,
)
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MiniICCSSeismogram construction block in validate_iccs_construction (lines 148–160) is an exact duplicate of the same block in create_iccs_instance (lines 105–117). This duplication increases maintenance risk: any future change to the construction arguments (e.g. a new field on MiniICCSSeismogram) must be applied in two places. Consider extracting the list comprehension into a shared private helper such as _build_mini_seismograms(event).

Copilot uses AI. Check for mistakes.
Comment thread src/aimbat/core/_iccs.py
event: AimbatEvent.

Raises:
Any exception raised by ICCS construction (e.g. invalid parameter values).
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Raises section in the validate_iccs_construction docstring says "Any exception raised by ICCS construction (e.g. invalid parameter values)." — this is a placeholder rather than a specific exception type, which violates the Google-style docstring convention used throughout the codebase (which lists concrete exception types). At minimum, the most likely exceptions (e.g. ValueError, pysmo configuration errors) should be listed, or the phrasing should follow the convention used elsewhere.

Suggested change
Any exception raised by ICCS construction (e.g. invalid parameter values).
ValueError: If ICCS construction fails due to invalid parameter values.
Exception: If ICCS construction fails for any other reason, such as
underlying pysmo configuration or runtime errors.

Copilot uses AI. Check for mistakes.
Comment thread CHANGELOG.md
- Move aimbat source to src directory
- Use pandas Timestamp and Timedelta
- Improve docstrings, io DI pattern, and data source terminology
- **(core)** Re-arange core, move set_default_event and friends out of core.
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog entry on line 22 contains a spelling error: "didn't" should read "didn't" (curly apostrophe vs straight apostrophe is acceptable, but "did't" is not present — the actual text "didn't do anything" is fine). However, line 88 contains "Re-arange" which should be "Re-arrange".

Suggested change
- **(core)** Re-arange core, move set_default_event and friends out of core.
- **(core)** Re-arrange core, move set_default_event and friends out of core.

Copilot uses AI. Check for mistakes.
Comment thread pyproject.toml Outdated
[dependency-groups]
test = [
"pytest>=8.4.2",
"pytest-asyncio>=1.3.0",
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pytest-asyncio was added to both pyproject.toml and uv.lock as a test dependency, but no tests in the repository use pytest.mark.asyncio or declare async def test_* functions. All async test code in test_tui.py uses asyncio.run() directly and does not require pytest-asyncio. This is an unnecessary dependency.

Suggested change
"pytest-asyncio>=1.3.0",

Copilot uses AI. Check for mistakes.
Comment thread src/aimbat/_cli/shell.py Outdated

# Check ICCS for whichever event was actually targeted by the command.
effective_flag = _extract_event_flag(tokens)
check_event_id = uuid.UUID(effective_flag) if effective_flag else shell_event_id
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On line 233, effective_flag is the raw string extracted by _extract_event_flag(), which may be a short UUID prefix (e.g. "abc12345") rather than a full UUID string. Calling uuid.UUID(effective_flag) on a short prefix will raise a ValueError that is not caught here, causing the shell to crash mid-loop instead of recovering gracefully. The _parse_event_id() helper (defined in the same file at line 58) already handles prefix resolution and should be used here instead of uuid.UUID() directly.

Suggested change
check_event_id = uuid.UUID(effective_flag) if effective_flag else shell_event_id
try:
check_event_id = (
_parse_event_id(effective_flag) if effective_flag else shell_event_id
)
except Exception as exc:
console.print(f"[red]{type(exc).__name__}:[/red] {exc}")
check_event_id = shell_event_id

Copilot uses AI. Check for mistakes.
@smlloyd smlloyd force-pushed the feat-shell branch 2 times, most recently from e199497 to d57af1d Compare March 6, 2026 04:30
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 6, 2026

Codecov Report

❌ Patch coverage is 43.37900% with 124 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.20%. Comparing base (571758b) to head (8a39c3c).
⚠️ Report is 2 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/aimbat/_cli/shell.py 29.71% 97 Missing ⚠️
src/aimbat/core/_iccs.py 36.84% 12 Missing ⚠️
src/aimbat/_tui/app.py 55.00% 9 Missing ⚠️
src/aimbat/core/_event.py 78.57% 3 Missing ⚠️
src/aimbat/_tui/modals.py 0.00% 2 Missing ⚠️
src/aimbat/core/_default_event.py 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #226      +/-   ##
==========================================
+ Coverage   69.14%   73.20%   +4.05%     
==========================================
  Files          50       51       +1     
  Lines        2940     3064     +124     
==========================================
+ Hits         2033     2243     +210     
+ Misses        907      821      -86     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@smlloyd smlloyd force-pushed the feat-shell branch 2 times, most recently from 892c1c7 to ef64d3c Compare March 6, 2026 10:19
- Implement 'aimbat shell' providing an interactive environment for project management
- Rewrite README.md to emphasize v2 goals, features, and modern tech stack
- Update documentation navigation and add comprehensive API usage snippets
- Refine CLI and TUI logic for default event handling
- Add functional tests for shell and TUI interfaces
- Update dependencies (adding pytest-asyncio) and improve test assertions
@smlloyd smlloyd merged commit bbe9460 into master Mar 6, 2026
23 of 24 checks passed
@smlloyd smlloyd deleted the feat-shell branch March 6, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants