Skip to content

Commit 9fbe568

Browse files
Integrate new example module into using the same UI that is build for Python SDK [skip:ci]
1 parent c346228 commit 9fbe568

File tree

7 files changed

+259
-2
lines changed

7 files changed

+259
-2
lines changed

src/aignostics/application/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212

1313
# advertise PageBuilder to enable auto-discovery
1414
if find_spec("nicegui"):
15+
from ._gui._frame import _frame
1516
from ._gui._page_builder import PageBuilder
1617

18+
# Create public alias for the frame function
19+
application_frame = _frame
20+
1721
__all__ += [
1822
"PageBuilder",
23+
"application_frame",
1924
]

src/aignostics/application/_gui/_page_index.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ async def _page_index(client: Client) -> None:
2020

2121
with ui.row().classes("p-4 pt-2 pr-0"), ui.column():
2222
await ui.context.client.connected()
23-
ui.label("Welcome to the Aignostics Launchpad").bind_text_from(
23+
ui.label("Hala walah to the Aignostics Launchpad").bind_text_from(
2424
app.storage.tab,
2525
"user_info",
2626
lambda user_info: (
27-
f"Welcome "
27+
f"Hala walah "
2828
f"{user_info.profile.given_name if hasattr(user_info, 'profile') and user_info.profile else ''} "
2929
f"to the Aignostics Launchpad"
3030
),
@@ -65,6 +65,12 @@ async def _page_index(client: Client) -> None:
6565
ui.label("Analyze results in Python Notebooks?").classes("text-xl")
6666
ui.label('On completed runs click "Marimo" to directly open the notebook.').classes("text")
6767

68+
ui.label("Want to see how modules work?").classes("text-xl")
69+
with ui.row().classes("text"):
70+
ui.label("Check out our")
71+
ui.link("Example Module", "/example").classes("text-blue-600 underline")
72+
ui.label("to understand the SDK architecture.")
73+
6874
with (
6975
ui.carousel(animated=True, arrows=True, navigation=True)
7076
.classes("flex-1 h-full m-0 p-0 self-end bg-[#423D6B] ")

src/aignostics/example/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""Example module for learning purposes."""
2+
3+
from ._cli import cli
4+
from ._service import Service
5+
6+
__all__ = [
7+
"Service",
8+
"cli",
9+
]
10+
11+
from importlib.util import find_spec
12+
13+
# advertise PageBuilder to enable auto-discovery
14+
if find_spec("nicegui"):
15+
from ._gui._page_builder import PageBuilder
16+
17+
__all__ += [
18+
"PageBuilder",
19+
]

src/aignostics/example/_cli.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""CLI of example module.
2+
3+
This module demonstrates how to create CLI commands using Typer that integrate
4+
with the main Aignostics CLI system. The CLI follows a modular pattern where
5+
each module can register its own commands.
6+
7+
Usage examples:
8+
uv run aignostics example hello
9+
uv run aignostics example hello "Your Name"
10+
uv run aignostics example data
11+
uv run aignostics example process "some text to process"
12+
"""
13+
14+
from typing import Annotated
15+
16+
import typer
17+
18+
from aignostics.utils import console, get_logger
19+
20+
from ._service import Service
21+
22+
logger = get_logger(__name__)
23+
24+
# Create a Typer instance for this module's CLI commands
25+
# - name="example": This becomes the subcommand name (aignostics example ...)
26+
# - help: Shown when user runs "aignostics example --help"
27+
# This CLI object is automatically discovered and registered with the main CLI
28+
# through the module's __init__.py file which exports it in __all__
29+
cli = typer.Typer(name="example", help="Example module commands")
30+
31+
32+
@cli.command()
33+
def hello(name: Annotated[str, typer.Argument(help="Name to greet")] = "World") -> None:
34+
"""Say hello to someone.
35+
36+
This is a simple command that demonstrates:
37+
- How to use Typer's @cli.command() decorator to register a function as a CLI command
38+
- How to use Annotated types for command arguments with help text
39+
- How to provide default values for optional arguments
40+
- How to use the console utility for colored output
41+
42+
Usage:
43+
uv run aignostics example hello # Uses default "World"
44+
uv run aignostics example hello "Alice" # Custom name
45+
46+
Args:
47+
name (str): Name to greet. This is a positional argument with a default value.
48+
"""
49+
# Use the console utility from aignostics.utils for rich text output
50+
# The [green] syntax is Rich markup for colored text
51+
console.print(f"[green]Hello {name} from Example module![/green]")
52+
53+
54+
@cli.command()
55+
def data() -> None:
56+
"""Get example data.
57+
58+
This command demonstrates:
59+
- How to create a command with no arguments
60+
- How to call service layer methods from CLI commands
61+
- How to format and display structured data in the terminal
62+
63+
Usage:
64+
uv run aignostics example data
65+
"""
66+
# Call the service layer to get data - this follows the separation of concerns pattern
67+
# where CLI commands are thin wrappers around business logic in the service layer
68+
example_data = Service.get_example_data()
69+
70+
# Display the data with formatting
71+
console.print("[blue]Example Data:[/blue]")
72+
for key, value in example_data.items():
73+
console.print(f" {key}: {value}")
74+
75+
76+
@cli.command()
77+
def process(text: Annotated[str, typer.Argument(help="Text to process")]) -> None:
78+
"""Process some text.
79+
80+
This command demonstrates:
81+
- How to use required positional arguments
82+
- How to pass user input to service layer methods
83+
- How to display processed results
84+
85+
Usage:
86+
uv run aignostics example process "Hello World"
87+
uv run aignostics example process "Any text you want to process"
88+
89+
Args:
90+
text (str): Text to process. This is a required positional argument.
91+
"""
92+
# Process the text using the service layer
93+
result = Service.process_example(text)
94+
95+
# Display the result with yellow coloring
96+
console.print(f"[yellow]{result}[/yellow]")
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""GUI page builder for example module."""
2+
3+
from aignostics.utils import BasePageBuilder
4+
5+
6+
class PageBuilder(BasePageBuilder):
7+
"""Page builder for example module."""
8+
9+
@staticmethod
10+
def register_pages() -> None:
11+
"""Register example module pages."""
12+
from nicegui import ui # noqa: PLC0415
13+
14+
@ui.page("/example")
15+
async def page_example() -> None:
16+
"""Example page."""
17+
from ._page_example import _page_example # noqa: PLC0415
18+
19+
await _page_example()
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Example page for the example module."""
2+
3+
from nicegui import ui
4+
5+
from aignostics.application import application_frame
6+
from aignostics.example import Service
7+
from aignostics.utils import get_logger
8+
9+
logger = get_logger(__name__)
10+
11+
# Constants
12+
SECTION_HEADER_CLASSES = "text-2xl font-semibold mb-4"
13+
14+
15+
async def _page_example() -> None:
16+
"""Example page displaying module functionality."""
17+
ui.page_title("Example Module")
18+
19+
# Set client content styling to work with frame layout
20+
ui.context.client.content.classes(remove="nicegui-content")
21+
ui.context.client.content.classes(add="pl-5 pt-5")
22+
23+
await application_frame(
24+
navigation_title="🔬 Example Module",
25+
navigation_icon="science",
26+
navigation_icon_color="primary",
27+
navigation_icon_tooltip="Example Module for learning SDK architecture",
28+
left_sidebar=True,
29+
)
30+
31+
with ui.row().classes("p-4 pt-2 pr-0"), ui.column().classes("w-full max-w-4xl"):
32+
# Header
33+
ui.label("🔬 Example Module").classes("text-4xl font-bold mb-4")
34+
ui.label("This is a template module for learning the SDK architecture.").classes("text-xl text-gray-600 mb-8")
35+
36+
# Example data section
37+
with ui.card().classes("w-full mb-6"):
38+
ui.label("📊 Example Data").classes(SECTION_HEADER_CLASSES)
39+
example_data = Service.get_example_data()
40+
41+
with ui.grid(columns=2).classes("w-full gap-4"):
42+
for key, value in example_data.items():
43+
ui.label(f"{key.title()}:").classes("font-medium")
44+
ui.label(value).classes("text-blue-600")
45+
46+
# Interactive section
47+
with ui.card().classes("w-full mb-6"):
48+
ui.label("🛠️ Text Processing").classes(SECTION_HEADER_CLASSES)
49+
50+
input_field = ui.input(label="Enter text to process", placeholder="Type something here...").classes(
51+
"w-full mb-4"
52+
)
53+
54+
result_area = ui.label("").classes("text-green-600 font-medium")
55+
56+
def process_text() -> None:
57+
"""Process the input text and display result."""
58+
if input_field.value:
59+
processed = Service.process_example(input_field.value)
60+
result_area.text = processed
61+
else:
62+
result_area.text = "Please enter some text first!"
63+
64+
ui.button("Process Text", on_click=process_text).classes("bg-blue-500 text-white")
65+
66+
# Navigation section
67+
with ui.card().classes("w-full"):
68+
ui.label("🧭 Navigation").classes(SECTION_HEADER_CLASSES)
69+
ui.label("This example module demonstrates:").classes("mb-2")
70+
71+
with ui.column().classes("ml-4"):
72+
ui.label("• Service layer with static methods")
73+
ui.label("• CLI commands (try: uv run aignostics example --help)")
74+
ui.label("• GUI page registration and routing")
75+
ui.label("• Module auto-discovery via dependency injection")
76+
77+
ui.button("← Back to Home", on_click=lambda: ui.navigate.to("/")).classes("mt-4")

src/aignostics/example/_service.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Service of the example module."""
2+
3+
from aignostics.utils import BaseService, get_logger
4+
5+
logger = get_logger(__name__)
6+
7+
8+
class Service(BaseService):
9+
"""Example service for demonstration purposes."""
10+
11+
def __init__(self) -> None:
12+
"""Initialize the example service."""
13+
super().__init__()
14+
logger.info("Example service initialized")
15+
16+
@staticmethod
17+
def get_example_data() -> dict[str, str]:
18+
"""Get some example data.
19+
20+
Returns:
21+
dict[str, str]: Example data dictionary.
22+
"""
23+
return {"message": "Hello from Example module!", "status": "active", "module": "example"}
24+
25+
@staticmethod
26+
def process_example(input_text: str) -> str:
27+
"""Process example input.
28+
29+
Args:
30+
input_text (str): Text to process.
31+
32+
Returns:
33+
str: Processed text.
34+
"""
35+
return f"Processed: {input_text}"

0 commit comments

Comments
 (0)