# Path: yawn/utilities/cron.py
# class Crontab:
#     def __init__(self, schedule: str):
#     def __repr__(self):
#     def next_run(self, current_time: datetime.datetime) -> datetime.datetime:
# def parse(pattern: str, max_value: int):
# def cron_validator(crontab: str):
# 
# Path: yawn/utilities/database.py
# def close_on_exception(func):
#     def wrapper(*args, **kwargs):
# def current_time():
# 
# Path: yawn/utilities/cron.py
# class Crontab:
#     """
#     Simplified Crontab
# 
#     Support "minute hour weekday" components of a standard cron job.
#     - "*/15 2,7,15 1-5" means "every fifteen minutes, on hours 2 7 15, Monday-Friday"
#     - Minutes are from 0-59, hours from 0-23, and days from 0(Sunday)-6(Saturday)
#     - Fields can contain multiple comma-separated values
#     - Values can be an integer or repeating pattern of the '*/2' variety
#     """
# 
#     def __init__(self, schedule: str):
#         self.schedule = schedule
#         components = schedule.split(' ')
#         if len(components) != 3:
#             raise ValueError('Crontab must be three space-delimited components')
# 
#         minutes, hours, weekdays = components
#         self.minutes = parse(minutes, 60)
#         self.hours = parse(hours, 24)
#         self.weekdays = parse(weekdays, 24)
# 
#     def __repr__(self):
#         return '<Crontab: {}>'.format(self.schedule)
# 
#     def next_run(self, current_time: datetime.datetime) -> datetime.datetime:
#         """Given the current time, when is the next scheduled run?"""
#         # if next run is next day, get smallest hour, smallest minute
#         # if next run is today, future hour, get smallest minute
#         # if next run is today, this hour, get next greatest minute
#         next_run = datetime.datetime(current_time.year, current_time.month, current_time.day,
#                                      tzinfo=current_time.tzinfo)
#         weekday = current_time.isoweekday()
#         weekday = 0 if weekday == 7 else weekday  # Move Sunday to day 0
#         if weekday in self.weekdays:
#             # could be a run today
#             if current_time.hour in self.hours:
#                 # could be a run this hour
#                 for minute in self.minutes:
#                     if minute > current_time.minute:
#                         # there is a run this hour
#                         return next_run.replace(hour=current_time.hour, minute=minute)
#             # no run this hour, check future hours
#             for hour in self.hours:
#                 if hour > current_time.hour:
#                     # there is a run today
#                     return next_run.replace(hour=hour, minute=self.minutes[0])
#         # no run today, look for next matching weekday
#         for day in range(1, 7):
#             next_run += datetime.timedelta(days=1)
#             weekday = next_run.isoweekday()
#             weekday = 0 if weekday == 7 else weekday  # Move Sunday to day 0
#             if weekday in self.weekdays:
#                 return next_run.replace(hour=self.hours[0], minute=self.minutes[0])
#         raise RuntimeError('No next run found for schedule {}'.format(self.schedule))


from django.db import models
from django.contrib.postgres import fields
from django.db.models import functions
from yawn.utilities import cron, database
from yawn.utilities.cron import Crontab
        from yawn.task.models import Task
        from yawn.task.models import Task
            version = self.current_version.version

            # disable the past schedule
            self.current_version.schedule_active = False
            self.current_version.save()

        workflow = Workflow.objects.create(name=self, version=version + 1, **kwargs)
        self.current_version = workflow
        self.save()
        return workflow


class Workflow(models.Model):
    class Meta:
        unique_together = (('name', 'version'),)

    name = models.ForeignKey(WorkflowName, models.PROTECT)
    version = models.IntegerField(editable=False)  # serializer read-only

    # scheduling is completely optional:
    schedule_active = models.BooleanField(default=False)
    schedule = models.TextField(null=True, validators=[cron.cron_validator])
    next_run = models.DateTimeField(null=True)

    # parameters
    parameters = fields.JSONField(default=dict)

    def save(self, **kwargs):
        if self.schedule_active:
            if not self.next_run:

# Path: yawn/utilities/cron.py
# class Crontab:
#     def __init__(self, schedule: str):
#     def __repr__(self):
#     def next_run(self, current_time: datetime.datetime) -> datetime.datetime:
# def parse(pattern: str, max_value: int):
# def cron_validator(crontab: str):
# 
# Path: yawn/utilities/database.py
# def close_on_exception(func):
#     def wrapper(*args, **kwargs):
# def current_time():
# 
# Path: yawn/utilities/cron.py
# class Crontab:
#     """
#     Simplified Crontab
# 
#     Support "minute hour weekday" components of a standard cron job.
#     - "*/15 2,7,15 1-5" means "every fifteen minutes, on hours 2 7 15, Monday-Friday"
#     - Minutes are from 0-59, hours from 0-23, and days from 0(Sunday)-6(Saturday)
#     - Fields can contain multiple comma-separated values
#     - Values can be an integer or repeating pattern of the '*/2' variety
#     """
# 
#     def __init__(self, schedule: str):
#         self.schedule = schedule
#         components = schedule.split(' ')
#         if len(components) != 3:
#             raise ValueError('Crontab must be three space-delimited components')
# 
#         minutes, hours, weekdays = components
#         self.minutes = parse(minutes, 60)
#         self.hours = parse(hours, 24)
#         self.weekdays = parse(weekdays, 24)
# 
#     def __repr__(self):
#         return '<Crontab: {}>'.format(self.schedule)
# 
#     def next_run(self, current_time: datetime.datetime) -> datetime.datetime:
#         """Given the current time, when is the next scheduled run?"""
#         # if next run is next day, get smallest hour, smallest minute
#         # if next run is today, future hour, get smallest minute
#         # if next run is today, this hour, get next greatest minute
#         next_run = datetime.datetime(current_time.year, current_time.month, current_time.day,
#                                      tzinfo=current_time.tzinfo)
#         weekday = current_time.isoweekday()
#         weekday = 0 if weekday == 7 else weekday  # Move Sunday to day 0
#         if weekday in self.weekdays:
#             # could be a run today
#             if current_time.hour in self.hours:
#                 # could be a run this hour
#                 for minute in self.minutes:
#                     if minute > current_time.minute:
#                         # there is a run this hour
#                         return next_run.replace(hour=current_time.hour, minute=minute)
#             # no run this hour, check future hours
#             for hour in self.hours:
#                 if hour > current_time.hour:
#                     # there is a run today
#                     return next_run.replace(hour=hour, minute=self.minutes[0])
#         # no run today, look for next matching weekday
#         for day in range(1, 7):
#             next_run += datetime.timedelta(days=1)
#             weekday = next_run.isoweekday()
#             weekday = 0 if weekday == 7 else weekday  # Move Sunday to day 0
#             if weekday in self.weekdays:
#                 return next_run.replace(hour=self.hours[0], minute=self.minutes[0])
#         raise RuntimeError('No next run found for schedule {}'.format(self.schedule))


# Path: yawn/workflow/models.py
from django.db import models
from django.contrib.postgres import fields
from django.db.models import functions
from yawn.utilities import cron, database
from yawn.utilities.cron import Crontab
        from yawn.task.models import Task
        from yawn.task.models import Task

            version = self.current_version.version

            # disable the past schedule
            self.current_version.schedule_active = False
            self.current_version.save()

        workflow = Workflow.objects.create(name=self, version=version + 1, **kwargs)
        self.current_version = workflow
        self.save()
        return workflow


class Workflow(models.Model):
    class Meta:
        unique_together = (('name', 'version'),)

    name = models.ForeignKey(WorkflowName, models.PROTECT)
    version = models.IntegerField(editable=False)  # serializer read-only

    # scheduling is completely optional:
    schedule_active = models.BooleanField(default=False)
    schedule = models.TextField(null=True, validators=[cron.cron_validator])
    next_run = models.DateTimeField(null=True)

    # parameters
    parameters = fields.JSONField(default=dict)

    def save(self, **kwargs):
        if self.schedule_active:
            if not self.next_run:

                self.next_run = Crontab(self.schedule).next_run(database.current_time())