Skip to content

Feature/file based progress #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

teo-lohrer-su
Copy link
Collaborator

Implements file-based progress logging and regular progress state dump.
The following code provides a demo for this capability.

#!/usr/bin/env python3
"""
Multi-level progress demo for testing file-based progress CLI.

This creates a task that demonstrates progress tracking at multiple levels:
- Level 0: Overall task progress
- Level 1: Sub-phase progress
- Level 2: Detailed operation progress
"""

import logging
import time
from pathlib import Path
from experimaestro import Task, Param, experiment, progress
import click

logging.basicConfig(level=logging.INFO)


class MultiLevelProgressTask(Task):
    """Task that demonstrates multi-level progress tracking"""

    phases: Param[int] = 3
    """Number of phases to execute"""

    operations_per_phase: Param[int] = 5
    """Number of operations per phase"""
    steps_per_operation: Param[int] = 30

    sleep_time: Param[float] = 0.5
    """Sleep time between operations (seconds)"""

    def execute(self):
        """Execute the task with multi-level progress reporting"""
        logging.info("Starting MultiLevelProgressTask")

        progress(0.0, level=0, desc="Starting task")

        for phase in range(self.phases):
            # Level 0: Overall progress across phases
            phase_progress = phase / self.phases
            progress(phase_progress, level=0, desc=f"Phase {phase + 1}/{self.phases}")

            # Level 1: Progress within current phase
            progress(0.0, level=1, desc=f"Starting phase {phase + 1}")

            for op in range(self.operations_per_phase):
                # Level 1: Progress within phase
                op_progress = op / self.operations_per_phase
                progress(
                    op_progress,
                    level=1,
                    desc=f"Phase {phase + 1} - operation {op + 1}/{self.operations_per_phase}",
                )

                # Level 2: Detailed operation progress
                for step in range(self.steps_per_operation):
                    step_progress = step / self.steps_per_operation
                    progress(
                        step_progress,
                        level=2,
                        desc=f"Operation {op + 1} - step {step + 1}/{self.steps_per_operation}",
                    )
                    time.sleep(self.sleep_time)

                # Complete the operation
                progress(1.0, level=2, desc=f"Operation {op + 1} complete")

            # Complete the phase
            progress(1.0, level=1, desc=f"Phase {phase + 1} complete")

        # Complete the task
        progress(1.0, level=0, desc="Task completed successfully")
        logging.info("MultiLevelProgressTask completed")


@click.option("--port", type=int, default=12345, help="Port for monitoring")
@click.option("--phases", type=int, default=3, help="L1: Number of phases")
@click.option("--operations", type=int, default=5, help="L2: Operations per phase")
@click.option("--steps", type=int, default=30, help="L3: Steps per operation")
@click.option(
    "--sleep-time", type=float, default=0.5, help="Sleep time between steps (seconds)"
)
@click.option("--clear", is_flag=True, help="Clear workdir before running")
@click.argument("workdir", type=Path)
@click.command()
def cli(port, phases, operations, steps, sleep_time, clear, workdir):
    """Run multi-level progress demo"""
    print(f"Starting progress demo in {workdir}")
    print(f"Phases: {phases}, Operations per phase: {operations}")
    print(f"Sleep time: {sleep_time}s, Port: {port}")
    # using rmtree
    if workdir.exists() and workdir.is_dir() and clear:
        from shutil import rmtree

        print(f"Clearing existing workdir: {workdir}")
        rmtree(workdir)

    # Create the workdir if it does not exist
    workdir.mkdir(parents=True, exist_ok=True)

    # Create experiment
    with experiment(workdir, "progress_demo", port=port) as xp:
        print(f"Experiment server running on port {xp.server.port}")
        print(f"Working directory: {xp.workdir}")

        # Submit the task
        task = MultiLevelProgressTask.C(
            phases=phases,
            operations_per_phase=operations,
            sleep_time=sleep_time,
            steps_per_operation=steps,
        ).submit()

        job = task.__xpm__.job

        # Print job information for CLI monitoring
        print("\nJob submitted!")
        print(f"Job path: {job.path}")

        # Extract task name and job hash for CLI commands
        task_name = job.path.parent.name
        job_hash = job.path.name
        job_id = f"{task_name}/{job_hash}"

        print(f"Job ID: {job_id}")
        print("\n=== CLI MONITORING COMMANDS ===")
        print("# Show current progress:")
        print(f"python -m experimaestro progress --workdir {workdir} show {job_id}")
        print("\n# Live progress with tqdm bars:")
        print(f"python -m experimaestro progress --workdir {workdir} live {job_id}")
        print("\n# List all jobs:")
        print(f"python -m experimaestro progress --workdir {workdir} list")
        print("=" * 70)


if __name__ == "__main__":
    cli()

@teo-lohrer-su teo-lohrer-su requested a review from bpiwowar July 17, 2025 12:35
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
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy