def test_delete_all(client_fixture: fixture) -> None:
    page = client_fixture.get(f"/api/delete")
    assert page.json == {"message": "Scheduler: all jobs deleted!"}
    assert page.status_code == 200

    # one maintenance job should be left
    assert len(atlas_scheduler.get_jobs()) == 0

    # add a job with no args
    from .conftest import demo_task

    atlas_scheduler.add_job(
        func=demo_task,
        trigger="interval",
        seconds=10,
        id="test job 2",
        name="test job 2",
        replace_existing=True,
    )

    # add a job and try again
    p_id, t_id = create_demo_task()
    page = client_fixture.get(f"/api/add/{t_id}")
    assert page.json == {"message": "Scheduler: task job added!"}
    assert page.status_code == 200

    page = client_fixture.get(f"/api/delete")
    assert page.json == {"message": "Scheduler: all jobs deleted!"}
    assert page.status_code == 200
    # one  job should be left
    assert len(atlas_scheduler.get_jobs()) == 1
def delete_all_tasks() -> Response:
    """Delete all scheduled tasks."""
    for job in atlas_scheduler.get_jobs():
        if re.match(r"^\d+-\d+-.+?$", job.id):
            job.remove()

    return jsonify({"message": "Scheduler: all jobs deleted!"})
def delete_orphans() -> Response:
    """Delete all orphaned jobs."""
    for job in atlas_scheduler.get_jobs():
        if job.args and Task.query.filter_by(id=int(job.args[0])).count() == 0:
            job.remove()

    return jsonify({"message": "Scheduler: orphans deleted!"})
def get_jobs_details() -> Response:
    """Get list of all jobs with all details."""
    return jsonify([{
        "name": job.name,
        "job_id": job.id,
        "next_run_time": job.next_run_time,
        "id": job.id.split("-")[1],
    } for job in atlas_scheduler.get_jobs()
                    if re.match(r"^\d+-\d+-.+?$", job.id)])
def test_delete_orphans(client_fixture: fixture) -> None:
    # run without any orphans
    page = client_fixture.get(f"/api/delete-orphans")
    assert page.json == {"message": "Scheduler: orphans deleted!"}
    assert page.status_code == 200

    # manually add a job to scheduler
    atlas_scheduler.add_job(
        func=demo_task,
        trigger="interval",
        seconds=10,
        id="test job 2",
        name="test job 2",
        args=["99"],
        replace_existing=True,
    )

    assert len(atlas_scheduler.get_jobs()) == 1

    # job with no args
    atlas_scheduler.add_job(
        func=demo_task,
        trigger="interval",
        seconds=10,
        id="test job 3",
        name="test job 3",
        replace_existing=True,
    )

    assert len(atlas_scheduler.get_jobs()) == 2

    page = client_fixture.get(f"/api/delete-orphans")
    assert page.json == {"message": "Scheduler: orphans deleted!"}
    assert page.status_code == 200

    assert len(atlas_scheduler.get_jobs()) == 1
def scheduler_delete_task(task_id: int) -> bool:
    """Delete all jobs associated with a task from the scheduler.

    :param task_id: id of task to delete associated jobs
    :returns: true
    """
    status = False
    for job in atlas_scheduler.get_jobs():
        if job.args and str(job.args[0]) == str(task_id):
            job.remove()
            status = True

    task = Task.query.filter_by(id=task_id).first()
    if task:
        task.next_run = None
        db.session.commit()

    return status
def test_run_task_with_delay(client_fixture: fixture) -> None:
    p_id, t_id = create_demo_task()
    delay = 5
    page = client_fixture.get(f"/api/run/{t_id}/delay/{delay}")
    assert page.json == {"message": "Scheduler: task scheduled!"}
    assert page.status_code == 200

    # check that job is in scheduler
    scheduled_task = [
        x for x in atlas_scheduler.get_jobs()
        if len(x.args) > 0 and x.args[0] == str(t_id)
    ][0]
    assert (scheduled_task.next_run_time.replace(
        microsecond=0,
        second=0).isoformat() == (datetime.now() +
                                  timedelta(minutes=delay)).replace(
                                      microsecond=0,
                                      second=0,
                                      tzinfo=tzlocal()).isoformat())
def job_sync() -> None:
    """Job to sync run times between model and scheduler."""
    try:
        with atlas_scheduler.app.app_context():

            # remove next run date and duration from disabled jobs
            db.session.execute(
                update(Task).where(Task.enabled == 0 and
                                   (Task.next_run is not None
                                    or Task.est_duration is not None)).values(
                                        next_run=None, est_duration=None))
            db.session.commit()

            # remove disabled jobs from the scheduler
            scheduler_jobs = atlas_scheduler.get_jobs()

            for task in Task.query.filter_by(enabled=0).all():
                for job in scheduler_jobs:
                    if job.args and str(job.args[0]) == str(task.id):
                        job.remove()

    # pylint: disable=broad-except
    except BaseException as e:
        print(str(e))  # noqa: T001
def schedule() -> Response:
    """Build simulated run schedule.

    Build list of hours to show on the chart:
    ['now', <now + 1>, <now + 2>, etc]

    Build list of schedule for next 24 hours

    Merge two lists and put 0 where needed.
    """
    now = datetime.datetime.now(
        datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo)
    tomorrow = now + datetime.timedelta(hours=24)

    hour_list = ["now"]

    now_int = now

    while now_int < tomorrow:
        now_int = now_int + datetime.timedelta(hours=1)
        hour_list.append(datetime.datetime.strftime(now_int, "%-H:00"))

    active_schedule = []

    for job in atlas_scheduler.get_jobs():
        if (job.id == "job_sync" or not hasattr(job, "next_run_time")
                or job.next_run_time is None and job.args):
            continue

        job_date = job.next_run_time

        while job_date and job_date < tomorrow:

            if now.replace(minute=0, second=0,
                           microsecond=0) == job_date.replace(minute=0,
                                                              second=0,
                                                              microsecond=0):
                message = "now"

            else:
                message = datetime.datetime.strftime(job_date, "%-H:00")

            active_schedule.append({"message": message, "date": job_date})

            if not job_date.tzinfo:  # pragma: no cover
                job_date.astimezone()

            job_date = job.trigger.get_next_fire_time(job_date, job_date)

    active_schedule.sort(key=lambda active_schedule: active_schedule["date"])
    groups = {
        key: list(group)
        for key, group in groupby(
            active_schedule,
            lambda active_schedule: active_schedule["message"])
    }
    active_schedule = []

    for hour in hour_list:
        active_schedule.append({
            "case":
            hour,
            "count": (sum(1 for x in groups.get(hour))
                      if groups.get(hour) else 0),  # type: ignore[union-attr]
        })

    return jsonify(active_schedule)
def get_scheduled_jobs() -> Response:
    """Get list of all scheduled job ids."""
    return jsonify([
        int(job.id.split("-")[1]) for job in atlas_scheduler.get_jobs()
        if job.next_run_time is not None and re.match(r"^\d+-\d+-.+?$", job.id)
    ])
def get_jobs() -> Response:
    """Get list of all job ids."""
    return jsonify([
        int(job.id.split("-")[1]) for job in atlas_scheduler.get_jobs()
        if re.match(r"^\d+-\d+-.+?$", job.id)
    ])