# 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
class WorkflowName(models.Model):
    name = models.TextField(unique=True)
    current_version = models.OneToOneField('Workflow', on_delete=models.CASCADE,
                                           null=True, related_name='is_current')

    def new_version(self, **kwargs):
        """Create a new version of a workflow"""
        version = 0
        if self.current_version_id:
            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)

# 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

class WorkflowName(models.Model):
    name = models.TextField(unique=True)
    current_version = models.OneToOneField('Workflow', on_delete=models.CASCADE,
                                           null=True, related_name='is_current')

    def new_version(self, **kwargs):
        """Create a new version of a workflow"""
        version = 0
        if self.current_version_id:
            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])