# Path: amivapi/cron.py
# class NotSchedulable(Exception):
# def schedulable(func):
# def periodic(period, *args):
#     def wrap(func):
#         def wrapped():
# def schedule_task(time, func, *args):
# def update_scheduled_task(time, func, *args):
# def schedule_once_soon(func, *args):
# def func_str(func):
# def run_scheduled_tasks():
# def init_app(app):
# 
# Path: amivapi/cron.py
# class NotSchedulable(Exception):
#     pass
# 
# def periodic(period, *args):
#     """ Decorator to mark a function to be executed periodically.
#     Args:
#         period: timedelta object describing the time between two calls
#         args: arguments to be passed every time
#     """
#     def wrap(func):
#         @wraps(func)
#         def wrapped():
#             schedule_task(datetime.utcnow() + period, wrapped)
#             func(*args)
# 
#         schedulable(wrapped)
# 
#         # if init_app has already run, schedule the first execution
#         if current_app:
#             schedule_once_soon(wrapped)
#         # As this decorator is run very early, there might not be an app yet.
#         # Therefore we save the functions to a list to be scheduled on app init.
#         periodic_functions.append(wrapped)
# 
#         return wrapped
#     return wrap
# 
# def run_scheduled_tasks():
#     """ Check for scheduled task, which have passed the deadline and run them.
#     This needs an app context.
#     """
#     while True:
#         task = (current_app.data.driver.db['scheduled_tasks']
#                 .find_one_and_delete(
#                     {'time': {'$lte': datetime.utcnow()}}))
# 
#         if task is None:
#             return
# 
#         args = pickle.loads(task['args'])
#         func = schedulable_functions[task['function']]
#         func(*args)
# 
# def schedulable(func):
#     """ Registers a function to be in the table of schedulable functions.
#     This is necessary, as we can not save references to python functions in the
#     database.
#     """
#     schedulable_functions[func_str(func)] = func
#     return func
# 
# def schedule_once_soon(func, *args):
#     """ Schedules a function to be run as soon as the scheduler is run the next
#     time. Also check, that it is not already scheduled to be run first.
#     """
#     if current_app.data.driver.db['scheduled_tasks'].count_documents(
#             {'function': func_str(func)}) != 0:
#         return
#     schedule_task(datetime.utcnow(), func, *args)
# 
# def schedule_task(time, func, *args):
#     """ Schedule a task at some point in the future. """
#     func_s = func_str(func)
# 
#     if func_s not in schedulable_functions:
#         raise NotSchedulable("%s is not schedulable. Did you forget the "
#                              "@schedulable decorator?" % func.__name__)
# 
#     current_app.data.driver.db['scheduled_tasks'].insert_one({
#         'time': time,
#         'function': func_s,
#         'args': pickle.dumps(args)
#     })
# 
# def update_scheduled_task(time, func, *args):
#     """ Update a scheduled task that was previously registered. """
#     func_s = func_str(func)
# 
#     if func_s not in schedulable_functions:
#         raise NotSchedulable("%s is not schedulable. Did you forget the "
#                              "@schedulable decorator?" % func.__name__)
# 
#     current_app.data.driver.db['scheduled_tasks'].update_one({
#         'function': func_s
#     },
#         {'$set': {
#                  'time': time,
#                  'args': pickle.dumps(args)
#         }})
# 
# Path: amivapi/tests/utils.py
# class WebTestNoAuth(WebTest):
#     """WebTest without authentification."""
# 
#     def setUp(self, **extra_config):
#         """Use auth hook to always authenticate as root for every request."""
#         super().setUp(**extra_config)
# 
#         def authenticate_root(resource):
#             g.resource_admin = True
# 
#         self.app.after_auth += authenticate_root


from datetime import datetime, timedelta
from freezegun import freeze_time
from amivapi import cron
from amivapi.cron import (
    NotSchedulable,
    periodic,
    run_scheduled_tasks,
    schedulable,
    schedule_once_soon,
    schedule_task,
    update_scheduled_task
)
from amivapi.tests.utils import WebTestNoAuth
class CronTest(WebTestNoAuth):

    has_run = False
    received_arg = None
    run_count = 0

    def setUp(self):
        CronTest.has_run = False
        CronTest.received_arg = None
        CronTest.run_count = 0

        super().setUp()

    def test_scheduled_function_gets_called(self):
        with self.app.app_context(), freeze_time(
                "2016-01-01 00:00:00") as frozen_time:
            @schedulable
            def scheduled_function(arg):
                CronTest.has_run = True
                CronTest.received_arg = arg

            schedule_task(datetime(2016, 1, 1, 1, 0, 0),
                          scheduled_function,
                          "arg")

            run_scheduled_tasks()

            self.assertFalse(CronTest.has_run)

            frozen_time.tick(delta=timedelta(hours=1))

# Path: amivapi/cron.py
# class NotSchedulable(Exception):
# def schedulable(func):
# def periodic(period, *args):
#     def wrap(func):
#         def wrapped():
# def schedule_task(time, func, *args):
# def update_scheduled_task(time, func, *args):
# def schedule_once_soon(func, *args):
# def func_str(func):
# def run_scheduled_tasks():
# def init_app(app):
# 
# Path: amivapi/cron.py
# class NotSchedulable(Exception):
#     pass
# 
# def periodic(period, *args):
#     """ Decorator to mark a function to be executed periodically.
#     Args:
#         period: timedelta object describing the time between two calls
#         args: arguments to be passed every time
#     """
#     def wrap(func):
#         @wraps(func)
#         def wrapped():
#             schedule_task(datetime.utcnow() + period, wrapped)
#             func(*args)
# 
#         schedulable(wrapped)
# 
#         # if init_app has already run, schedule the first execution
#         if current_app:
#             schedule_once_soon(wrapped)
#         # As this decorator is run very early, there might not be an app yet.
#         # Therefore we save the functions to a list to be scheduled on app init.
#         periodic_functions.append(wrapped)
# 
#         return wrapped
#     return wrap
# 
# def run_scheduled_tasks():
#     """ Check for scheduled task, which have passed the deadline and run them.
#     This needs an app context.
#     """
#     while True:
#         task = (current_app.data.driver.db['scheduled_tasks']
#                 .find_one_and_delete(
#                     {'time': {'$lte': datetime.utcnow()}}))
# 
#         if task is None:
#             return
# 
#         args = pickle.loads(task['args'])
#         func = schedulable_functions[task['function']]
#         func(*args)
# 
# def schedulable(func):
#     """ Registers a function to be in the table of schedulable functions.
#     This is necessary, as we can not save references to python functions in the
#     database.
#     """
#     schedulable_functions[func_str(func)] = func
#     return func
# 
# def schedule_once_soon(func, *args):
#     """ Schedules a function to be run as soon as the scheduler is run the next
#     time. Also check, that it is not already scheduled to be run first.
#     """
#     if current_app.data.driver.db['scheduled_tasks'].count_documents(
#             {'function': func_str(func)}) != 0:
#         return
#     schedule_task(datetime.utcnow(), func, *args)
# 
# def schedule_task(time, func, *args):
#     """ Schedule a task at some point in the future. """
#     func_s = func_str(func)
# 
#     if func_s not in schedulable_functions:
#         raise NotSchedulable("%s is not schedulable. Did you forget the "
#                              "@schedulable decorator?" % func.__name__)
# 
#     current_app.data.driver.db['scheduled_tasks'].insert_one({
#         'time': time,
#         'function': func_s,
#         'args': pickle.dumps(args)
#     })
# 
# def update_scheduled_task(time, func, *args):
#     """ Update a scheduled task that was previously registered. """
#     func_s = func_str(func)
# 
#     if func_s not in schedulable_functions:
#         raise NotSchedulable("%s is not schedulable. Did you forget the "
#                              "@schedulable decorator?" % func.__name__)
# 
#     current_app.data.driver.db['scheduled_tasks'].update_one({
#         'function': func_s
#     },
#         {'$set': {
#                  'time': time,
#                  'args': pickle.dumps(args)
#         }})
# 
# Path: amivapi/tests/utils.py
# class WebTestNoAuth(WebTest):
#     """WebTest without authentification."""
# 
#     def setUp(self, **extra_config):
#         """Use auth hook to always authenticate as root for every request."""
#         super().setUp(**extra_config)
# 
#         def authenticate_root(resource):
#             g.resource_admin = True
# 
#         self.app.after_auth += authenticate_root


# Path: amivapi/tests/test_cron.py
from datetime import datetime, timedelta
from freezegun import freeze_time
from amivapi import cron
from amivapi.cron import (
    NotSchedulable,
    periodic,
    run_scheduled_tasks,
    schedulable,
    schedule_once_soon,
    schedule_task,
    update_scheduled_task
)
from amivapi.tests.utils import WebTestNoAuth

class CronTest(WebTestNoAuth):

    has_run = False
    received_arg = None
    run_count = 0

    def setUp(self):
        CronTest.has_run = False
        CronTest.received_arg = None
        CronTest.run_count = 0

        super().setUp()

    def test_scheduled_function_gets_called(self):
        with self.app.app_context(), freeze_time(
                "2016-01-01 00:00:00") as frozen_time:
            @schedulable
            def scheduled_function(arg):
                CronTest.has_run = True
                CronTest.received_arg = arg

            schedule_task(datetime(2016, 1, 1, 1, 0, 0),
                          scheduled_function,
                          "arg")

            run_scheduled_tasks()

            self.assertFalse(CronTest.has_run)

            frozen_time.tick(delta=timedelta(hours=1))

            run_scheduled_tasks()