Skip to content

docs: add "Get started with Console" user guide - #712

Open
omid-aignostics wants to merge 2 commits into
mainfrom
docs/get-started-console-guide
Open

docs: add "Get started with Console" user guide#712
omid-aignostics wants to merge 2 commits into
mainfrom
docs/get-started-console-guide

Conversation

@omid-aignostics

Copy link
Copy Markdown
Collaborator

…ong-running, skip:test:matrix-runner, skip:test:very-long-running]

Adds a guide for the flow where slides are uploaded to the organization's bucket with the Python SDK and analyzed in Console at platform.aignostics.com, which is easier for non-technical users than the Launchpad GUI.

Structure follows sign up -> upload your slides -> analyze your slides with Atlas H&E-TME, with five screenshots of Console (create run form, slide selection from the bucket, run parameters, viewer overlays, result download), the 30-day result retention window, and a troubleshooting section.

Links the guide from the docs index and the interface table in README.

@omid-aignostics
omid-aignostics requested a review from a team as a code owner August 12, 2026 12:57
@omid-aignostics
omid-aignostics force-pushed the docs/get-started-console-guide branch from 9b65d09 to f16146a Compare August 12, 2026 14:44
@omid-aignostics omid-aignostics changed the title docs: add "Get started with Console" user guide [skip:ci, skip:test:l… docs: add "Get started with Console" user guide Aug 12, 2026
Adds a guide for the flow where slides are uploaded to the organization's
bucket with the Python SDK and analyzed in Console at
platform.aignostics.com, which is easier for non-technical users than the
Launchpad GUI.

Structure follows sign up -> upload your slides -> analyze your slides
with Atlas H&E-TME, with five screenshots of Console (create run form,
slide selection from the bucket, run parameters, viewer overlays, result
download), the 30-day result retention window, and a troubleshooting
section.

Links the guide from the docs index and the interface table in README.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@omid-aignostics
omid-aignostics force-pushed the docs/get-started-console-guide branch from f16146a to c5719f7 Compare August 12, 2026 14:46
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1022 3 1019 15
View the top 2 failed test(s) by shortest run time
tests.aignostics.qupath.gui_test::test_gui_run_qupath_install_to_inspect
Stack Traces | 2.24s run time
user = <nicegui.testing.user.User object at 0x7f4227b48490>
runner = <typer.testing.CliRunner object at 0x7f4232abb3e0>
tmp_path = PosixPath('.../pytest-of-runner/pytest-21/test_gui_run_qupath_install_to0')
silent_logging = None, qupath_teardown = None, qupath_save_restore = None
record_property = <function record_property.<locals>.append_property at 0x7f4271844040>

    @pytest.mark.e2e
    @pytest.mark.long_running
    @pytest.mark.skipif(
        (platform.system() == "Linux" and platform.machine() in {"aarch64", "arm64"}),
        reason="QuPath is not supported on ARM64 Linux",
    )
    @pytest.mark.timeout(timeout=60 * 15)
    @pytest.mark.sequential
    async def test_gui_run_qupath_install_to_inspect(  # noqa: C901, PLR0912, PLR0913, PLR0915
        user: User,
        runner: CliRunner,
        tmp_path: Path,
        silent_logging: None,
        qupath_teardown: None,
        qupath_save_restore: None,
        record_property,
    ) -> None:
        """Test installing QuPath, downloading run results, creating QuPath project from it, and inspecting results."""
        record_property("tested-item-id", "TC-QUPATH-01, SPEC-GUI-SERVICE")
    
        # Find run
        runs = Service().application_runs(
            application_id=HETA_APPLICATION_ID,
            application_version=HETA_APPLICATION_VERSION,
            external_id=SPOT_0_GS_URL,
            tags=["scheduled"],
            has_output=True,
            limit=1,
        )
        if not runs:
            message = f"No matching runs found for application {HETA_APPLICATION_ID} ({HETA_APPLICATION_VERSION}). "
            message += "This test requires the scheduled test test_application_runs_heta_version passing first."
            pytest.skip(message)
    
        run_id = runs[0].run_id
    
        # Explore run
        run = Service().application_run(run_id).details()
        print(
            f"Found existing run: {run.run_id}\n"
            f"application: {run.application_id} ({run.version_number})\n"
            f"status: {run.state}, output: {run.output}\n"
            f"submitted at: {run.submitted_at}, terminated at: {run.terminated_at}\n"
            f"statistics: {run.statistics!r}\n",
            f"custom_metadata: {run.custom_metadata!r}\n",
        )
    
        # Explore results
        results = list(Service().application_run(run_id).results())
        assert results, f"No results found for run {run_id}"
        for item in results:
            print(
                f"Found item: {item.item_id}, status: {item.state}, output: {item.output}, "
                f"external_id: {item.external_id}\n"
                f"custom_metadata: {item.custom_metadata!r}\n",
            )
    
        with patch(
            "aignostics.application._gui._page_application_run_describe.get_user_data_directory", return_value=tmp_path
        ):
            # Step 1: (Re)Install QuPath
            result = runner.invoke(cli, ["qupath", "install"])
            output = normalize_output(result.output, strip_ansi=True)
>           assert f"QuPath v{QUPATH_VERSION} installed successfully" in output, (
                f"Expected 'QuPath v{QUPATH_VERSION} installed successfully' in output.\nOutput: {output}"
            )
E           AssertionError: Expected 'QuPath v0.6.0 installed successfully' in output.
E             Output: Installing QuPath version 0.6.0 to ................../home/runner/.local/share/aignostics...Downloading ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0 bytes  ?  Extracting  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0 bytes  ?  Failed to install QuPath version 0.6.0 at ................../home/runner/.local/share/aignostics: Failed to install QuPath v0.6.0 to '................../home/runner/.local/share/aignostics': Failed to download QuPath from https://github..../releases/download/v0.6.0/QuPath-v0.6.0-Linux.tar.xz.
E           assert 'QuPath v0.6.0 installed successfully' in "Installing QuPath version 0.6.0 to ................../home/runner/.local/share/aignostics...Downloading ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0 bytes  ?  Extracting  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0 bytes  ?  Failed to install QuPath version 0.6.0 at ................../home/runner/.local/share/aignostics: Failed to install QuPath v0.6.0 to '................../home/runner/.local/share/aignostics': Failed to download QuPath from https://github..../releases/download/v0.6.0/QuPath-v0.6.0-Linux.tar.xz."

.../aignostics/qupath/gui_test.py:201: AssertionError
tests.aignostics.dataset.gui_test::test_gui_idc_downloads
Stack Traces | 123s run time
user = <nicegui.testing.user.User object at 0x7f5c575bc6e0>
tmp_path = PosixPath('.../pytest-20/popen-gw7/test_gui_idc_downloads0')
silent_logging = None
record_property = <function record_property.<locals>.append_property at 0x7f5c73c01dd0>

    @pytest.mark.e2e
    @pytest.mark.long_running
    @pytest.mark.flaky(retries=1, delay=5, only_on=[AssertionError])
    @pytest.mark.timeout(timeout=60 * 5)
    async def test_gui_idc_downloads(user: User, tmp_path: Path, silent_logging: bool, record_property) -> None:
        """Test that the user can download a dataset to a temporary directory."""
        record_property("tested-item-id", "TC-DATASET-GUI-01, SPEC-GUI-SERVICE")
        # Mock get_user_data_directory to return the tmpdir for this test
        with patch("aignostics.dataset._gui.get_user_data_directory", return_value=tmp_path):
            await user.open("/dataset/idc")
    
            await user.should_see(marker="BUTTON_EXAMPLE_DATASET")
            user.find(marker="BUTTON_EXAMPLE_DATASET").click()
            await user.should_see("1.3.6.1.4.1.5962.99.1.1069745200.1645485340.1637452317744.2.0")
    
            await user.should_see(marker="SOURCE_INPUT")
            user.find(marker="SOURCE_INPUT").clear()
            user.find(marker="SOURCE_INPUT").type("1.3.6.1.4.1.5962.99.1.1038911754.1238045814.1637421484298.15.0")
            await user.should_see("1.3.6.1.4.1.5962.99.1.1038911754.1238045814.1637421484298.15.0")
    
            await user.should_see(marker="BUTTON_DOWNLOAD_DESTINATION")
            user.find(marker="BUTTON_DOWNLOAD_DESTINATION").click()
    
            await user.should_see(marker="BUTTON_FILEPICKER_CANCEL")
            user.find(marker="BUTTON_FILEPICKER_CANCEL").click()
            await user.should_see(MESSAGE_NO_DOWNLOAD_FOLDER_SELECTED)
    
            await user.should_see(marker="BUTTON_DOWNLOAD_DESTINATION_DATA")
            user.find(marker="BUTTON_DOWNLOAD_DESTINATION_DATA").click()
            await user.should_not_see(MESSAGE_NO_DOWNLOAD_FOLDER_SELECTED)
    
            await user.should_see(marker="BUTTON_DOWNLOAD")
            user.find(marker="BUTTON_DOWNLOAD").click()
            await assert_notified(user, "Downloading", wait_seconds=5)
    
>           await assert_notified(user, "Download completed", wait_seconds=120)

.../aignostics/dataset/gui_test.py:57: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

user = <nicegui.testing.user.User object at 0x7f5c575bc6e0>
expected_notification = 'Download completed', wait_seconds = 120

    async def assert_notified(user: User, expected_notification: str, wait_seconds: int = 5) -> str:
        """Check if the user receives a notification within the specified time.
    
        This utility function helps test GUI notifications by waiting for a specific
        notification message to appear in the user's notification messages.
    
        Args:
            user: The nicegui User instance for testing.
            expected_notification: The notification text to look for (partial match).
            wait_seconds: Maximum time to wait for the notification (default: 5).
    
        Returns:
            str: The oldest matching notification message found.
    
        Raises:
            pytest.fail: If no matching notification is found within the wait time.
        """
        for _ in range(wait_seconds):
            matching_messages = [msg for msg in user.notify.messages if expected_notification in msg]
            if matching_messages:
                return matching_messages[0]
            await sleep(1)
    
        recent_messages = (user.notify.messages[-10:] if len(user.notify.messages) > 10 else user.notify.messages)[::-1]
        total_count = len(user.notify.messages)
>       pytest.fail(
            f"No notification containing '{expected_notification}' was found within {wait_seconds} seconds. "
            f"Total messages: {total_count}. Recent messages: {recent_messages}"
        )
E       Failed: No notification containing 'Download completed' was found within 120 seconds. Total messages: 3. Recent messages: ["Download failed: 'NoneType' object is not subscriptable", 'Downloading 1.3.6.1.4.1.5962.99.1.1038911754.1238045814.1637421484298.15.0 ...', 'You did not make a selection. You must choose a download folder.']

tests/conftest.py:131: Failed
View the full list of 1 ❄️ flaky test(s)
tests.aignostics.qupath.gui_test::test_gui_qupath_install_and_launch

Flake rate in main: 9.09% (Passed 30 times, Failed 3 times)

Stack Traces | 17s run time
runner = <typer.testing.CliRunner object at 0x7f4227d27890>

    @pytest.fixture
    def qupath_save_restore(runner: CliRunner) -> Generator[None, None, None]:
        """Uninstall QuPath for clean state, restore after test if it was installed."""
        result = runner.invoke(cli, ["qupath", "uninstall"])
        assert result.exit_code in {0, 2}, (
            f"Unexpected exit code {result.exit_code} from 'qupath uninstall': {result.output}"
        )
        was_installed = result.exit_code == 0
        yield
        if was_installed:
            reinstall_result = runner.invoke(cli, ["qupath", "install"])
            if reinstall_result.exit_code != 0:
>               pytest.fail(
                    f"Failed to reinstall QuPath in qupath_save_restore fixture "
                    f"(exit code {reinstall_result.exit_code}). Output:\n{reinstall_result.output}"
                )
E               Failed: Failed to reinstall QuPath in qupath_save_restore fixture (exit code 1). Output:
E               Installing QuPath version 0.6.0 to ........./home/runner/.local/share/aignostics...
E               Downloading ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0 bytes  ?  
E               Extracting  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━   0 bytes  ?  
E               Failed to install QuPath version 0.6.0 at ........./home/runner/.local/share/aignostics: Failed to install QuPath v0.6.0 to '........./home/runner/.local/share/aignostics': Failed to download QuPath from https://github..../releases/download/v0.6.0/QuPath-v0.6.0-Linux.tar.xz.

.../aignostics/qupath/conftest.py:23: Failed

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

The MCP server is not yet usable for customers, so advertising it in the
documentation sends readers to instructions they cannot act on.

Excludes get_started_mcp.rst from the Sphinx build, drops it from the
"Get started" toctree, and comments out the MCP row in the interface
table. The guide and its partial stay in the repository; each change
carries a note on how to restore it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant