From b57e344ef5a39aa0e505d67351a954ea0db4da40 Mon Sep 17 00:00:00 2001 From: muhab Date: Mon, 8 Sep 2025 13:37:37 +0200 Subject: [PATCH 1/4] Integrate new example module into using the same UI that is build for Python SDK --- src/aignostics/application/__init__.py | 5 + .../application/_gui/_page_index.py | 10 +- src/aignostics/example/__init__.py | 19 ++++ src/aignostics/example/_cli.py | 96 +++++++++++++++++++ src/aignostics/example/_gui/_page_builder.py | 19 ++++ src/aignostics/example/_gui/_page_example.py | 77 +++++++++++++++ src/aignostics/example/_service.py | 35 +++++++ 7 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 src/aignostics/example/__init__.py create mode 100644 src/aignostics/example/_cli.py create mode 100644 src/aignostics/example/_gui/_page_builder.py create mode 100644 src/aignostics/example/_gui/_page_example.py create mode 100644 src/aignostics/example/_service.py diff --git a/src/aignostics/application/__init__.py b/src/aignostics/application/__init__.py index 77589880..666ca546 100644 --- a/src/aignostics/application/__init__.py +++ b/src/aignostics/application/__init__.py @@ -11,8 +11,13 @@ # advertise PageBuilder to enable auto-discovery if find_spec("nicegui"): + from ._gui._frame import _frame from ._gui._page_builder import PageBuilder + # Create public alias for the frame function + application_frame = _frame + __all__ += [ "PageBuilder", + "application_frame", ] diff --git a/src/aignostics/application/_gui/_page_index.py b/src/aignostics/application/_gui/_page_index.py index 9b25feac..c9e933ac 100644 --- a/src/aignostics/application/_gui/_page_index.py +++ b/src/aignostics/application/_gui/_page_index.py @@ -40,11 +40,11 @@ async def _page_index(client: Client, query: str | None = None) -> None: with ui.row().classes("p-4 pt-2 pr-0"), ui.column(): await ui.context.client.connected() - ui.label("Welcome to the Aignostics Launchpad").bind_text_from( + ui.label("Hala walah to the Aignostics Launchpad").bind_text_from( app.storage.tab, "user_info", lambda user_info: ( - f"Welcome " + f"Hala walah " f"{user_info.profile.given_name if hasattr(user_info, 'profile') and user_info.profile else ''} " f"to the Aignostics Launchpad" ), @@ -85,6 +85,12 @@ async def _page_index(client: Client, query: str | None = None) -> None: ui.label("Analyze results in Python Notebooks?").classes("text-xl") ui.label('On completed runs click "Marimo" to directly open the notebook.').classes("text") + ui.label("Want to see how modules work?").classes("text-xl") + with ui.row().classes("text"): + ui.label("Check out our") + ui.link("Example Module", "/example").classes("text-blue-600 underline") + ui.label("to understand the SDK architecture.") + with ( ui.carousel(animated=True, arrows=True, navigation=True) .classes("flex-1 h-full m-0 p-0 self-end bg-[#423D6B] ") diff --git a/src/aignostics/example/__init__.py b/src/aignostics/example/__init__.py new file mode 100644 index 00000000..798d0e74 --- /dev/null +++ b/src/aignostics/example/__init__.py @@ -0,0 +1,19 @@ +"""Example module for learning purposes.""" + +from ._cli import cli +from ._service import Service + +__all__ = [ + "Service", + "cli", +] + +from importlib.util import find_spec + +# advertise PageBuilder to enable auto-discovery +if find_spec("nicegui"): + from ._gui._page_builder import PageBuilder + + __all__ += [ + "PageBuilder", + ] diff --git a/src/aignostics/example/_cli.py b/src/aignostics/example/_cli.py new file mode 100644 index 00000000..ffa088c9 --- /dev/null +++ b/src/aignostics/example/_cli.py @@ -0,0 +1,96 @@ +"""CLI of example module. + +This module demonstrates how to create CLI commands using Typer that integrate +with the main Aignostics CLI system. The CLI follows a modular pattern where +each module can register its own commands. + +Usage examples: + uv run aignostics example hello + uv run aignostics example hello "Your Name" + uv run aignostics example data + uv run aignostics example process "some text to process" +""" + +from typing import Annotated + +import typer + +from aignostics.utils import console, get_logger + +from ._service import Service + +logger = get_logger(__name__) + +# Create a Typer instance for this module's CLI commands +# - name="example": This becomes the subcommand name (aignostics example ...) +# - help: Shown when user runs "aignostics example --help" +# This CLI object is automatically discovered and registered with the main CLI +# through the module's __init__.py file which exports it in __all__ +cli = typer.Typer(name="example", help="Example module commands") + + +@cli.command() +def hello(name: Annotated[str, typer.Argument(help="Name to greet")] = "World") -> None: + """Say hello to someone. + + This is a simple command that demonstrates: + - How to use Typer's @cli.command() decorator to register a function as a CLI command + - How to use Annotated types for command arguments with help text + - How to provide default values for optional arguments + - How to use the console utility for colored output + + Usage: + uv run aignostics example hello # Uses default "World" + uv run aignostics example hello "Alice" # Custom name + + Args: + name (str): Name to greet. This is a positional argument with a default value. + """ + # Use the console utility from aignostics.utils for rich text output + # The [green] syntax is Rich markup for colored text + console.print(f"[green]Hello {name} from Example module![/green]") + + +@cli.command() +def data() -> None: + """Get example data. + + This command demonstrates: + - How to create a command with no arguments + - How to call service layer methods from CLI commands + - How to format and display structured data in the terminal + + Usage: + uv run aignostics example data + """ + # Call the service layer to get data - this follows the separation of concerns pattern + # where CLI commands are thin wrappers around business logic in the service layer + example_data = Service.get_example_data() + + # Display the data with formatting + console.print("[blue]Example Data:[/blue]") + for key, value in example_data.items(): + console.print(f" {key}: {value}") + + +@cli.command() +def process(text: Annotated[str, typer.Argument(help="Text to process")]) -> None: + """Process some text. + + This command demonstrates: + - How to use required positional arguments + - How to pass user input to service layer methods + - How to display processed results + + Usage: + uv run aignostics example process "Hello World" + uv run aignostics example process "Any text you want to process" + + Args: + text (str): Text to process. This is a required positional argument. + """ + # Process the text using the service layer + result = Service.process_example(text) + + # Display the result with yellow coloring + console.print(f"[yellow]{result}[/yellow]") diff --git a/src/aignostics/example/_gui/_page_builder.py b/src/aignostics/example/_gui/_page_builder.py new file mode 100644 index 00000000..960cff90 --- /dev/null +++ b/src/aignostics/example/_gui/_page_builder.py @@ -0,0 +1,19 @@ +"""GUI page builder for example module.""" + +from aignostics.utils import BasePageBuilder + + +class PageBuilder(BasePageBuilder): + """Page builder for example module.""" + + @staticmethod + def register_pages() -> None: + """Register example module pages.""" + from nicegui import ui # noqa: PLC0415 + + @ui.page("/example") + async def page_example() -> None: + """Example page.""" + from ._page_example import _page_example # noqa: PLC0415 + + await _page_example() diff --git a/src/aignostics/example/_gui/_page_example.py b/src/aignostics/example/_gui/_page_example.py new file mode 100644 index 00000000..442400d9 --- /dev/null +++ b/src/aignostics/example/_gui/_page_example.py @@ -0,0 +1,77 @@ +"""Example page for the example module.""" + +from nicegui import ui + +from aignostics.application import application_frame +from aignostics.example import Service +from aignostics.utils import get_logger + +logger = get_logger(__name__) + +# Constants +SECTION_HEADER_CLASSES = "text-2xl font-semibold mb-4" + + +async def _page_example() -> None: + """Example page displaying module functionality.""" + ui.page_title("Example Module") + + # Set client content styling to work with frame layout + ui.context.client.content.classes(remove="nicegui-content") + ui.context.client.content.classes(add="pl-5 pt-5") + + await application_frame( + navigation_title="🔬 Example Module", + navigation_icon="science", + navigation_icon_color="primary", + navigation_icon_tooltip="Example Module for learning SDK architecture", + left_sidebar=True, + ) + + with ui.row().classes("p-4 pt-2 pr-0"), ui.column().classes("w-full max-w-4xl"): + # Header + ui.label("🔬 Example Module").classes("text-4xl font-bold mb-4") + ui.label("This is a template module for learning the SDK architecture.").classes("text-xl text-gray-600 mb-8") + + # Example data section + with ui.card().classes("w-full mb-6"): + ui.label("📊 Example Data").classes(SECTION_HEADER_CLASSES) + example_data = Service.get_example_data() + + with ui.grid(columns=2).classes("w-full gap-4"): + for key, value in example_data.items(): + ui.label(f"{key.title()}:").classes("font-medium") + ui.label(value).classes("text-blue-600") + + # Interactive section + with ui.card().classes("w-full mb-6"): + ui.label("🛠️ Text Processing").classes(SECTION_HEADER_CLASSES) + + input_field = ui.input(label="Enter text to process", placeholder="Type something here...").classes( + "w-full mb-4" + ) + + result_area = ui.label("").classes("text-green-600 font-medium") + + def process_text() -> None: + """Process the input text and display result.""" + if input_field.value: + processed = Service.process_example(input_field.value) + result_area.text = processed + else: + result_area.text = "Please enter some text first!" + + ui.button("Process Text", on_click=process_text).classes("bg-blue-500 text-white") + + # Navigation section + with ui.card().classes("w-full"): + ui.label("🧭 Navigation").classes(SECTION_HEADER_CLASSES) + ui.label("This example module demonstrates:").classes("mb-2") + + with ui.column().classes("ml-4"): + ui.label("• Service layer with static methods") + ui.label("• CLI commands (try: uv run aignostics example --help)") + ui.label("• GUI page registration and routing") + ui.label("• Module auto-discovery via dependency injection") + + ui.button("← Back to Home", on_click=lambda: ui.navigate.to("/")).classes("mt-4") diff --git a/src/aignostics/example/_service.py b/src/aignostics/example/_service.py new file mode 100644 index 00000000..40069df8 --- /dev/null +++ b/src/aignostics/example/_service.py @@ -0,0 +1,35 @@ +"""Service of the example module.""" + +from aignostics.utils import BaseService, get_logger + +logger = get_logger(__name__) + + +class Service(BaseService): + """Example service for demonstration purposes.""" + + def __init__(self) -> None: + """Initialize the example service.""" + super().__init__() + logger.info("Example service initialized") + + @staticmethod + def get_example_data() -> dict[str, str]: + """Get some example data. + + Returns: + dict[str, str]: Example data dictionary. + """ + return {"message": "Hello from Example module!", "status": "active", "module": "example"} + + @staticmethod + def process_example(input_text: str) -> str: + """Process example input. + + Args: + input_text (str): Text to process. + + Returns: + str: Processed text. + """ + return f"Processed: {input_text}" From 09b182c8443d329e1b097e9456ad9704a3db51a3 Mon Sep 17 00:00:00 2001 From: muhab Date: Mon, 10 Nov 2025 10:49:11 +0100 Subject: [PATCH 2/4] add claude code new workflow --- .github/workflows/claude-code-doc-review.yml | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/claude-code-doc-review.yml diff --git a/.github/workflows/claude-code-doc-review.yml b/.github/workflows/claude-code-doc-review.yml new file mode 100644 index 00000000..6922dce6 --- /dev/null +++ b/.github/workflows/claude-code-doc-review.yml @@ -0,0 +1,49 @@ +name: "+ Claude Code / Documentation Review" + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + documentation-review: + name: Review doc changes + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Analyze requirements impact + uses: anthropics/claude-code-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + prompt: | + Review this PR and check if requirements/specs need updates. + + **Your task:** + + 1. Run git diff to see what changed + 2. Identify which modules changed (application, platform, wsi, etc.) + 3. Read relevant files from requirements/ and specifications/ directories + 4. Report which docs might need updates + + **Output format:** + + ## Changed Files + - List changed files + - Identify affected modules + + ## Requirements/Specs to Update + - filename: 1-sentence why it needs updating + + Keep it simple and concise. + claude_args: >- + --max-turns 20 + --model claude-sonnet-4-5-20250929 From b4fce36668c56d0fcc91dbdc8ede5b0a5d2bf04f Mon Sep 17 00:00:00 2001 From: muhab Date: Mon, 10 Nov 2025 13:27:51 +0100 Subject: [PATCH 3/4] refine claude-code-doc-review. --- .github/workflows/claude-code-doc-review.yml | 41 ++++++++++++++------ 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/.github/workflows/claude-code-doc-review.yml b/.github/workflows/claude-code-doc-review.yml index 6922dce6..bb91230a 100644 --- a/.github/workflows/claude-code-doc-review.yml +++ b/.github/workflows/claude-code-doc-review.yml @@ -25,25 +25,44 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} prompt: | - Review this PR and check if requirements/specs need updates. + Review this PR and analyze impact on requirements and specifications documentation. **Your task:** - 1. Run git diff to see what changed - 2. Identify which modules changed (application, platform, wsi, etc.) - 3. Read relevant files from requirements/ and specifications/ directories - 4. Report which docs might need updates + 1. Run git diff to see what code changed + 2. Identify which modules were affected (application, platform, wsi, bucket, dataset, etc.) + 3. Read relevant files from: + - requirements/ directory (SHR-*.md and SWR-*.md files) + - specifications/ directory (SPEC-*.md files) + 4. Analyze if existing docs need updates OR if new docs need to be created - **Output format:** + **IMPORTANT Rules:** + - ONLY analyze files in requirements/ and specifications/ directories + - SKIP template files like SPEC-MODULE-SERVICE-TEMPLATE.md + - SKIP any other documentation outside these directories + - If new documentation is needed, specify what file should be created following the existing naming pattern + - Follow the same structure/format as existing files in those directories - ## Changed Files + **Post a PR comment with this format:** + + ## 📋 Requirements & Specifications Review + + ### Changed Code - List changed files - - Identify affected modules + - Affected modules: [list] + + ### 📝 Existing Docs to Update + - `requirements/SHR-MODULE-X.md`: [1-sentence why it needs updating] + - `specifications/SPEC-MODULE-SERVICE.md`: [1-sentence why it needs updating] + + ### ➕ New Docs Needed + - **`requirements/SWR-MODULE-X-Y.md`**: [1-sentence describing what this new requirement should cover] + - **`specifications/SPEC-NEWMODULE-SERVICE.md`**: [1-sentence describing what this new spec should cover] - ## Requirements/Specs to Update - - filename: 1-sentence why it needs updating + ### ✅ Docs Still Accurate + - List requirements/specs that don't need changes - Keep it simple and concise. + Keep it concise and actionable. claude_args: >- --max-turns 20 --model claude-sonnet-4-5-20250929 From 3ecf71223bbad224ce4e37985c16d73a2a8cdac1 Mon Sep 17 00:00:00 2001 From: muhab Date: Mon, 10 Nov 2025 13:28:45 +0100 Subject: [PATCH 4/4] refine claude-code-doc-review. --- .github/workflows/claude-code-doc-review.yml | 68 ++++++++++++-------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/.github/workflows/claude-code-doc-review.yml b/.github/workflows/claude-code-doc-review.yml index bb91230a..9ff37c3c 100644 --- a/.github/workflows/claude-code-doc-review.yml +++ b/.github/workflows/claude-code-doc-review.yml @@ -25,44 +25,56 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} prompt: | - Review this PR and analyze impact on requirements and specifications documentation. + You are reviewing a pull request to ensure requirements and specifications documentation stays synchronized with code changes. - **Your task:** + **Analysis Process:** - 1. Run git diff to see what code changed - 2. Identify which modules were affected (application, platform, wsi, bucket, dataset, etc.) - 3. Read relevant files from: - - requirements/ directory (SHR-*.md and SWR-*.md files) - - specifications/ directory (SPEC-*.md files) - 4. Analyze if existing docs need updates OR if new docs need to be created + 1. Run `git diff origin/${{ github.base_ref }}...HEAD` to identify all changed files + 2. For each changed file, determine: + - Which module/component it belongs to + - What functionality was added, modified, or removed + 3. Read ALL existing files in: + - `requirements/` (SHR-*.md for stakeholder requirements, SWR-*.md for software requirements) + - `specifications/` (SPEC-*.md for software item specifications) + 4. Cross-reference code changes against documentation to identify: + - Docs that describe changed functionality (needs update) + - New functionality lacking documentation (needs new doc) + - Documentation that remains accurate (no action needed) - **IMPORTANT Rules:** - - ONLY analyze files in requirements/ and specifications/ directories - - SKIP template files like SPEC-MODULE-SERVICE-TEMPLATE.md - - SKIP any other documentation outside these directories - - If new documentation is needed, specify what file should be created following the existing naming pattern - - Follow the same structure/format as existing files in those directories + **Requirements:** + - Analyze ONLY `requirements/` and `specifications/` directories + - Ignore template files (files with "TEMPLATE" in the name) + - For new docs, follow existing naming conventions exactly + - Be specific about WHY each doc needs updating (cite the code change) - **Post a PR comment with this format:** + **Output Format:** + + Post a single PR comment using this exact structure: ## 📋 Requirements & Specifications Review - ### Changed Code - - List changed files - - Affected modules: [list] + ### 🔍 Code Changes Summary + **Modified files:** [list each file] + **Affected modules:** [comma-separated list] + + ### 📝 Documentation Updates Required + + #### Files Needing Updates: + - **`requirements/SHR-XXX.md`** - Reason: [specific code change that invalidates current doc] + - **`specifications/SPEC-YYY.md`** - Reason: [specific code change that invalidates current doc] - ### 📝 Existing Docs to Update - - `requirements/SHR-MODULE-X.md`: [1-sentence why it needs updating] - - `specifications/SPEC-MODULE-SERVICE.md`: [1-sentence why it needs updating] + #### New Documentation Needed: + - **`requirements/SWR-[MODULE]-[FEATURE].md`** - Coverage: [what new requirement this addresses] + - **`specifications/SPEC-[MODULE]-[SERVICE].md`** - Coverage: [what new implementation this describes] - ### ➕ New Docs Needed - - **`requirements/SWR-MODULE-X-Y.md`**: [1-sentence describing what this new requirement should cover] - - **`specifications/SPEC-NEWMODULE-SERVICE.md`**: [1-sentence describing what this new spec should cover] + ### ✅ Documentation Still Accurate + - `requirements/SHR-ZZZ.md` - Covers [feature], unaffected by changes + - `specifications/SPEC-AAA.md` - Covers [component], unaffected by changes - ### ✅ Docs Still Accurate - - List requirements/specs that don't need changes + ### 💡 Recommendations + [If applicable: suggest priority order for updates or note if changes are traceability-critical] - Keep it concise and actionable. + **Note:** If no documentation changes are needed, state: "✅ All requirements and specifications remain accurate." claude_args: >- --max-turns 20 - --model claude-sonnet-4-5-20250929 + --model claude-sonnet-4-5-20250929 \ No newline at end of file