Appointment.jobs 源代码

# YWolfeee:
# 本py文件保留所有需要与scheduler交互的函数。
from datetime import datetime, timedelta

from django.db import transaction

from Appointment.appoint.jobs import set_scheduler
from Appointment.config import appointment_config as CONFIG
from Appointment.extern.jobs import set_appoint_reminder
from Appointment.models import Appoint
from Appointment.utils.log import get_user_logger, logger, write_before_delete
from Appointment.utils.utils import get_conflict_appoints
from scheduler.periodic import periodical

'''
YWolfeee:
本py文件中的所有函数,或者发起了一个scheduler任务,或者删除了一个scheduler任务。
这些函数大多对应预约的开始、结束,微信的定时发送等。
如果需要实现新的函数,建议先详细阅读本py中其他函数的实现方式。
'''

__all__ = [
    'get_longterm_display',
    'add_longterm_appoint',
]


# 每周清除预约的程序,会写入logstore中
@periodical('cron', 'clear_appointments', day_of_week='sat',
            hour=3, minute=30, second=0,)
def clear_appointments():
    if CONFIG.delete_appoint_weekly:   # 是否清除一周之前的预约
        appoints_to_delete = Appoint.objects.filter(
            Afinish__lte=datetime.now()-timedelta(days=7))
        try:
            write_before_delete(appoints_to_delete)  # 删除之前写在记录内
            appoints_to_delete.delete()
        except Exception as e:
            return logger.warning(f"定时删除任务出现错误: {e}")

        # 写入日志
        logger.info("定时删除任务成功")


[文档] def get_longterm_display(times: int, interval_week: int, type: str = 'adj'): if type == 'adj': if interval_week == 1: longterm_info = f'{times}周的' elif interval_week == 2: longterm_info = f'{times}次单/双周的' else: longterm_info = f'{times}次间隔{interval_week}周的' else: if interval_week == 1: longterm_info = '每周一次' elif interval_week == 2: longterm_info = '隔周一次' else: longterm_info = f'每{interval_week}周一次' longterm_info += f' 共{times}次' return longterm_info
[文档] def add_longterm_appoint(appoint: 'Appoint | int', times: int, interval: int = 1, week_offset: int = None, admin: bool = False): ''' 自动开启事务以检查预约是否冲突,以原预约为模板直接生成新预约,不检查预约时间是否合法 appoint无效时可能出错,否则不出错 :param appoint: 预约的模板,Appoint类型视为可修改,不应再使用,否则作为主键 :type appoint: Appoint | int :param times: 长期预约次数 :type times: int :param interval: 每次预约间的间隔周数, defaults to 1 :type interval: int, optional :param week_offset: 首个预约距模板的周数,默认从模板后一次预约开始, defaults to None :type week_offset: int, optional :param admin: 以管理员权限创建,本参数暂被忽视, defaults to False :type admin: bool, optional :return: 首个冲突预约所在次数、以开始时间升序排列的冲突或生成的预约集合 :rtype: (int, QuerySet[Appoint]) | (None, QuerySet[Appoint]) ''' with transaction.atomic(): # 默认不包含传入预约当周 if week_offset is None: week_offset = interval # 获取模板 if not isinstance(appoint, Appoint): origin_pk = appoint appoint = Appoint.objects.get(pk=origin_pk) else: origin_pk = appoint.pk # 检查冲突 conflict_appoints = get_conflict_appoints( appoint, times, interval, week_offset, lock=True) if conflict_appoints: first_conflict = conflict_appoints[0] first_time = ((first_conflict.Afinish - appoint.Astart - timedelta(weeks=week_offset) ) // timedelta(weeks=interval) + 1) return first_time, conflict_appoints # 没有冲突,开始创建长线预约 students = appoint.students.all() new_appoints = [] new_appoint = appoint new_appoint.add_time(timedelta(weeks=week_offset)) for time in range(times): # 先获取复制对象的副本 new_appoint.Astatus = Appoint.Status.APPOINTED new_appoint.Atype = Appoint.Type.LONGTERM # 删除主键会被视为新对象,save时向数据库添加对象并更新主键 new_appoint.pk = None new_appoint.save() new_appoint.students_manager.set(students) new_appoints.append(new_appoint.pk) new_appoint.add_time(timedelta(weeks=interval)) # 获取长线预约集合,由于生成是按顺序的,默认排序也是按主键递增,无需重排 new_appoints = Appoint.objects.filter(pk__in=new_appoints) # 至此,预约都已成功创建,可以放心设置定时任务了,但设置定时任务出错也需要回滚 for new_appoint in new_appoints: set_scheduler(new_appoint) set_appoint_reminder(new_appoint) # 长线化预约发起成功,准备消息提示即可 longterm_info = get_longterm_display(times, interval) get_user_logger(appoint).info(f"发起{longterm_info}长线化预约, 原预约号为{origin_pk}") return None, new_appoints