def test_interval_schedule(self): serialized = { "start_date": { "dt": "2019-01-02T03:00:00", "tz": "UTC" }, "end_date": None, "interval": 3720000000, "__version__": "0.6.0+87.g44ac9ba5", "type": "IntervalSchedule", } schema = ScheduleSchema() schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime(2019, 1, 1)) == [ pendulum.datetime(2019, 1, 2, 3, 0, 0), pendulum.datetime(2019, 1, 2, 4, 2, 0), pendulum.datetime(2019, 1, 2, 5, 4, 0), pendulum.datetime(2019, 1, 2, 6, 6, 0), pendulum.datetime(2019, 1, 2, 7, 8, 0), pendulum.datetime(2019, 1, 2, 8, 10, 0), pendulum.datetime(2019, 1, 2, 9, 12, 0), pendulum.datetime(2019, 1, 2, 10, 14, 0), pendulum.datetime(2019, 1, 2, 11, 16, 0), pendulum.datetime(2019, 1, 2, 12, 18, 0), ]
def test_interval_clocks_with_sub_minute_intervals_cant_be_deserialized(): schema = ScheduleSchema() s = schedules.Schedule(clocks=[clocks.IntervalClock(timedelta(seconds=100))]) data = schema.dump(s) data["clocks"][0]["interval"] = 59 * 1e6 with pytest.raises(ValueError, match="can not be less than one minute"): schema.load(data)
def test_union_schedule(self): serialized = { "end_date": { "dt": "2020-01-01T00:00:00", "tz": "UTC" }, "schedules": [ { "end_date": { "dt": "2020-01-01T00:00:00", "tz": "UTC" }, "interval": 3720000000, "start_date": { "dt": "2019-01-02T03:00:00", "tz": "UTC" }, "__version__": "0.6.0+105.gce4aef06", "type": "IntervalSchedule", }, { "end_date": { "dt": "2020-01-01T00:00:00", "tz": "UTC" }, "cron": "3 0 * * *", "start_date": { "dt": "2019-01-02T03:00:00", "tz": "UTC" }, "__version__": "0.6.0+105.gce4aef06", "type": "CronSchedule", }, ], "start_date": { "dt": "2019-01-02T03:00:00", "tz": "UTC" }, "__version__": "0.6.0+105.gce4aef06", "type": "UnionSchedule", } schema = ScheduleSchema() with pytest.warns(UserWarning): schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime(2019, 1, 1)) == [ pendulum.datetime(2019, 1, 2, 3, 0, 0), pendulum.datetime(2019, 1, 2, 4, 2, 0), pendulum.datetime(2019, 1, 2, 5, 4, 0), pendulum.datetime(2019, 1, 2, 6, 6, 0), pendulum.datetime(2019, 1, 2, 7, 8, 0), pendulum.datetime(2019, 1, 2, 8, 10, 0), pendulum.datetime(2019, 1, 2, 9, 12, 0), pendulum.datetime(2019, 1, 2, 10, 14, 0), pendulum.datetime(2019, 1, 2, 11, 16, 0), pendulum.datetime(2019, 1, 2, 12, 18, 0), ]
def test_one_time_schedule_with_after(self): serialized = { "start_date": {"dt": "2019-01-02T03:00:00", "tz": "UTC"}, "__version__": "0.6.0+105.gce4aef06", "type": "OneTimeSchedule", } schema = ScheduleSchema() with pytest.warns(UserWarning): schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime(2020, 1, 1)) == []
def test_cron_schedule_with_end_date(self): serialized = { "end_date": {"dt": "2020-01-01T00:00:00", "tz": "UTC"}, "cron": "3 0 * * *", "start_date": {"dt": "2019-01-02T03:00:00", "tz": "UTC"}, "__version__": "0.6.0+105.gce4aef06", "type": "CronSchedule", } schema = ScheduleSchema() schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime(2019, 12, 31, 20)) == []
def test_one_time_schedule(self): serialized = { "start_date": { "dt": "2019-01-02T03:00:00", "tz": "UTC" }, "__version__": "0.6.0+105.gce4aef06", "type": "OneTimeSchedule", } schema = ScheduleSchema() schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime( 2019, 1, 1)) == [pendulum.datetime(2019, 1, 2, 3, 0, 0)]
async def test_interval_clock_units(self, run_query, flow_group_id): await run_query( query=self.mutation, variables=dict(input=dict( flow_group_id=flow_group_id, interval_clocks=[{ "interval": 60 }], )), ) flow_group = await models.FlowGroup.where(id=flow_group_id ).first({"schedule"}) deserialized_schedule = ScheduleSchema().load(flow_group.schedule) next_two = deserialized_schedule.next(n=2) assert next_two[0].add(minutes=1) == next_two[1]
def test_cron_schedule_hourly_daylight_savings_time_backward( self, serialize): """ 11/4/2018, at 2am, America/New_York switched clocks back an hour. """ dt = pendulum.datetime(2018, 11, 3, 23, tz="America/New_York") s = schedules.CronSchedule("0 * * * *", dt) if serialize: s = ScheduleSchema().load(s.serialize()) next_4 = s.next(4, after=dt) # repeat the 1am run in local time assert [t.in_tz("America/New_York").hour for t in next_4] == [0, 1, 2, 3] # runs every hour UTC assert [t.in_tz("UTC").hour for t in next_4] == [4, 6, 7, 8]
def test_cron_schedule_hourly_daylight_savings_time_forward( self, serialize): """ On 3/11/2018, at 2am, America/New_York switched clocks forward an hour. """ dt = pendulum.datetime(2018, 3, 10, 23, tz="America/New_York") s = schedules.CronSchedule("0 * * * *", dt) if serialize: s = ScheduleSchema().load(s.serialize()) next_4 = s.next(4, after=dt) # skip 2am assert [t.in_tz("America/New_York").hour for t in next_4] == [0, 1, 3, 4] # constant hourly schedule in utc time assert [t.in_tz("UTC").hour for t in next_4] == [5, 6, 7, 8]
def test_cron_schedule_daily_start_daylight_savings_time_backward( self, serialize): """ On 11/4/2018, at 2am, America/New_York switched clocks back an hour. Confirm that a schedule for 9am America/New_York stays 9am through the switch. """ dt = pendulum.datetime(2018, 11, 1, 9, tz="America/New_York") s = schedules.CronSchedule("0 9 * * *", dt) if serialize: s = ScheduleSchema().load(s.serialize()) next_4 = s.next(4, after=dt) # constant 9am start assert [t.in_tz("America/New_York").hour for t in next_4] == [9, 9, 9, 9] assert [t.in_tz("UTC").hour for t in next_4] == [13, 13, 14, 14]
async def test_set_flow_group_schedule(self, flow_group_id, clock): flow_group = await models.FlowGroup.where(id=flow_group_id ).first({"schedule"}) assert flow_group.schedule is None success = await api.flow_groups.set_flow_group_schedule( flow_group_id=flow_group_id, clocks=[clock]) assert success is True flow_group = await models.FlowGroup.where(id=flow_group_id ).first({"schedule"}) assert flow_group.schedule["clocks"][0] == clock schema = ScheduleSchema() schedule = schema.load(flow_group.schedule) assert schedule.clocks[0].start_date is None
async def test_add_cron_and_interval_clocks_to_flow_group_schedule_with_timezone( self, run_query, flow_group_id): result = await run_query( query=self.mutation, variables=dict(input=dict( flow_group_id=flow_group_id, cron_clocks=[{ "cron": "42 0 0 * * *" }], interval_clocks=[{ "interval": 4200 }], timezone="US/Pacific", )), ) assert result.data.set_flow_group_schedule.success is True flow_group = await models.FlowGroup.where(id=flow_group_id ).first({"schedule"}) schedule = ScheduleSchema().load(flow_group.schedule) assert len(schedule.clocks) == 2 assert all( [c.start_date <= pendulum.now("utc") for c in schedule.clocks]) assert all([ c.start_date.timezone_name == "US/Pacific" for c in schedule.clocks ])
def test_interval_schedule_with_end_date(self): serialized = { "interval": 3720000000, "start_date": {"dt": "2019-01-02T03:00:00", "tz": "UTC"}, "end_date": {"dt": "2020-01-01T00:00:00", "tz": "UTC"}, "__version__": "0.6.0+87.g44ac9ba5", "type": "IntervalSchedule", } schema = ScheduleSchema() schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime(2019, 12, 31, 20)) == [ pendulum.datetime(2019, 12, 31, 20, 36, 0), pendulum.datetime(2019, 12, 31, 21, 38, 0), pendulum.datetime(2019, 12, 31, 22, 40, 0), pendulum.datetime(2019, 12, 31, 23, 42, 0), ]
def test_interval_schedule_daily_start_daylight_savings_time_forward( self, serialize): """ On 3/11/2018, at 2am, America/New_York switched clocks forward an hour. Confirm that a schedule for 9am America/New_York stays 9am through the switch. """ dt = pendulum.datetime(2018, 3, 8, 9, tz="America/New_York") s = schedules.IntervalSchedule(dt, timedelta(days=1)) if serialize: s = ScheduleSchema().load(s.serialize()) next_4 = s.next(4, after=dt) # constant 9am start assert [t.in_tz("America/New_York").hour for t in next_4] == [9, 9, 9, 9] # utc time shifts assert [t.in_tz("UTC").hour for t in next_4] == [14, 14, 13, 13]
async def test_set_flow_group_schedule_respects_passed_timezones( self, flow_group_id, clock): flow_group = await models.FlowGroup.where(id=flow_group_id ).first({"schedule"}) assert flow_group.schedule is None success = await api.flow_groups.set_flow_group_schedule( flow_group_id=flow_group_id, clocks=[clock], timezone="US/Pacific") assert success is True schema = ScheduleSchema() flow_group = await models.FlowGroup.where(id=flow_group_id ).first({"schedule"}) schedule = schema.load(flow_group.schedule) assert schedule.clocks[0].start_date <= pendulum.now("utc") assert schedule.clocks[0].start_date.timezone_name == "US/Pacific"
def test_cron_schedule(self): serialized = { "cron": "3 0 * * *", "start_date": None, "end_date": None, "__version__": "0.6.0+87.g44ac9ba5", "type": "CronSchedule", } schema = ScheduleSchema() schedule = schema.load(serialized) assert schedule.next(10, after=pendulum.datetime(2019, 1, 1)) == [ pendulum.datetime(2019, 1, 1, 0, 3, 0), pendulum.datetime(2019, 1, 2, 0, 3, 0), pendulum.datetime(2019, 1, 3, 0, 3, 0), pendulum.datetime(2019, 1, 4, 0, 3, 0), pendulum.datetime(2019, 1, 5, 0, 3, 0), pendulum.datetime(2019, 1, 6, 0, 3, 0), pendulum.datetime(2019, 1, 7, 0, 3, 0), pendulum.datetime(2019, 1, 8, 0, 3, 0), pendulum.datetime(2019, 1, 9, 0, 3, 0), pendulum.datetime(2019, 1, 10, 0, 3, 0), ]
def serialize(self) -> tuple: from prefect.serialization.schedule import ScheduleSchema return ScheduleSchema().dump(self)
def test_interval_clocks_with_sub_minute_intervals_cant_be_serialized(): schema = ScheduleSchema() s = schedules.Schedule(clocks=[clocks.IntervalClock(timedelta(seconds=59))]) with pytest.raises(ValueError, match="can not be less than one minute"): schema.dump(s)
# Licensed under the Prefect Community License, available at # https://www.prefect.io/legal/prefect-community-license import pendulum import prefect from prefect_server import api, utilities from prefect_server.database import hasura, models from prefect.serialization.schedule import ScheduleSchema logger = utilities.logging.get_logger("api.schedules") schedule_schema = ScheduleSchema() async def set_active(schedule_id: str) -> bool: """ Sets a flow schedule to active Args: - schedule_id (str): the schedule ID Returns: bool: if the update succeeded """ result = await models.Schedule.where(id=schedule_id ).update(set={"active": True}) if not result.affected_rows: return False await schedule_flow_runs(schedule_id=schedule_id)
def serialize_and_deserialize(schedule: schedules.Schedule): schema = ScheduleSchema() return schema.load(json.loads(json.dumps(schema.dump(schedule))))