def img_get_func(request): if request.session.get("img_path", None) is not None: # 有缓存 return request.session['img_path'], False # 设置当头像无法加载时的位置 default_img_name = 'pipi_square_iRGk72U.jpg' img_path = global_info.this_url + "/media/avatar/" + default_img_name # 尝试加载头像 try: if global_info.account_auth: Sid = request.session['Sid'] urls = global_info.img_url + "/getStuImg?stuId=" + Sid img_get = long_request.post(url=urls, verify=False, timeout=3) if img_get.status_code == 200: # 接收到了学生信息 img_path = eval( img_get.content.decode('unicode-escape'))['path'] img_path = global_info.login_url + img_path # 存入缓存 request.session['img_path'] = img_path return img_path, True except: utils.operation_writer( global_info.system_log, f"从YPPF获取头像失败,原因需要查看代码", "web_func.img_get_func", "Problem") return img_path, False # 接受失败,返回旧地址 utils.operation_writer( global_info.system_log, f"从YPPF获取头像失败,未登录或未返回", "web_func.img_get_func", "Problem") return img_path, False
def refresh_scheduler(self, request, queryset): ''' 假设的情况是后台修改了开始和结束时间后,需要重置定时任务 因此,旧的定时任务可能处于任何完成状态 ''' if not request.user.is_superuser: return self.message_user(request=request, message='操作失败,没有权限,请联系老师!', level=messages.WARNING) if not queryset: return self.message_user(request=request, message='请至少选择一个需要更新的预约!', level=messages.WARNING) for appoint in queryset: try: aid = appoint.Aid start = appoint.Astart finish = appoint.Afinish if start > finish: return self.message_user( request=request, message=f'操作失败,预约{aid}开始和结束时间冲突!请勿篡改数据!', level=messages.WARNING) scheduler_func.cancel_scheduler(aid) # 注销原有定时任务 无异常 scheduler_func.set_scheduler(appoint) # 开始时进入进行中 结束后判定 if datetime.now() < start: # 如果未开始,修改开始提醒 scheduler_func.set_start_wechat(appoint, notify_new=False) except Exception as e: operation_writer(global_info.system_log, "出现更新定时任务失败的问题: " + str(e), "admin.longterm", "Error") return self.message_user(request=request, message=str(e), level=messages.WARNING) return self.message_user(request, '定时任务更新成功!')
def violate(self, request, queryset): # 确认违约 if not request.user.is_superuser: return self.message_user(request=request, message='操作失败,没有权限,请联系老师!', level=messages.WARNING) try: for appoint in queryset: assert not (appoint.Astatus == Appoint.Status.VIOLATED and appoint.Areason == Appoint.Reason.R_ELSE) ori_status = appoint.get_status() # if appoint.Astatus == Appoint.Status.WAITING: # 已违规时不扣除信用分,仅提示用户 if appoint.Astatus != Appoint.Status.VIOLATED: appoint.Astatus = Appoint.Status.VIOLATED appoint.major_student.Scredit -= 1 # 只扣除发起人 appoint.major_student.save() appoint.Areason = Appoint.Reason.R_ELSE # for stu in appoint.students.all(): # stu.Scredit -= 1 # stu.save() appoint.save() # send wechat message scheduler.add_job( send_wechat_message, args=[ [appoint.major_student.Sid], # stuid_list appoint.Astart, # start_time appoint.Room, # room "violate_admin", # message_type appoint.major_student.Sname, # major_student appoint.Ausage, # usage appoint.Aannouncement, appoint.Ayp_num + appoint.Anon_yp_num, f'原状态:{ori_status}', # reason #appoint.major_student.Scredit, ], id=f'{appoint.Aid}_violate_admin_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) # 5s足够了 operation_writer( global_info.system_log, str(appoint.Aid) + "号预约被管理员设为违约" + "发起人:" + str(appoint.major_student), "admin.violate", "OK") except: return self.message_user(request=request, message='操作失败!只允许对未审核的条目操作!', level=messages.WARNING) return self.message_user(request, "设为违约成功!")
def clear_appointments(): if global_info.delete_appoint_weekly: # 是否清除一周之前的预约 appoints_to_delete = Appoint.objects.filter( Afinish__lte=datetime.now()-timedelta(days=7)) try: # with transaction.atomic(): //不采取原子操作 write_before_delete(appoints_to_delete) # 删除之前写在记录内 appoints_to_delete.delete() except Exception as e: utils.operation_writer(global_info.system_log, "定时删除任务出现错误: "+str(e), "func[clear_appointments]", "Problem") # 写入日志 utils.operation_writer(global_info.system_log, "定时删除任务成功", "func[clear_appointments]")
def set_scheduler(appoint): '''不负责发送微信,不处理已经结束的预约,不处理始末逆序的预约,可以任何时间点调用,应该不报错''' # --- written by pht: 统一设置预约定时任务 --- # start = appoint.Astart finish = appoint.Afinish current_time = datetime.now() + timedelta(seconds=5) if finish < start: # 开始晚于结束,预约不合规 utils.operation_writer( global_info.system_log, f'预约{appoint.Aid}时间为{start}<->{finish},未能设置定时任务', 'scheduler_func.set_scheduler', 'Error') return False # 直接返回,预约不需要设置 if finish < current_time: # 预约已经结束 utils.operation_writer(global_info.system_log, f'预约{appoint.Aid}在设置定时任务时已经结束', 'scheduler_func.set_scheduler', 'Error') return False # 直接返回,预约不需要设置 has_started = start < current_time if has_started: # 临时预约或特殊情况下设置任务时预约可能已经开始 start = current_time # 改为立刻执行 # --- written end (2021.8.31) --- # # written by dyh: 在Astart将状态变为PROCESSING if not (has_started and appoint.Astatus == Appoint.Status.PROCESSING): scheduler.add_job(web_func.startAppoint, args=[appoint.Aid], id=f'{appoint.Aid}_start', replace_existing=True, next_run_time=start) # write by cdf start2 # 添加定时任务:finish scheduler.add_job(web_func.finishAppoint, args=[appoint.Aid], id=f'{appoint.Aid}_finish', replace_existing=True, next_run_time=finish) return True
def startAppoint(Aid): # 开始预约时的定时程序 try: appoint = Appoint.objects.get(Aid=Aid) except: utils.operation_writer( global_info.system_log, f"预约{str(Aid)}意外消失", "web_func.startAppoint", "Error") if appoint.Astatus == Appoint.Status.APPOINTED: # 顺利开始 appoint.Astatus = Appoint.Status.PROCESSING appoint.save() utils.operation_writer( global_info.system_log, f"预约{str(Aid)}成功开始: 状态变为进行中", "web_func.startAppoint") elif appoint.Astatus == Appoint.Status.PROCESSING: # 已经开始 utils.operation_writer( global_info.system_log, f"预约{str(Aid)}在检查时已经开始", "web_func.startAppoint") elif appoint.Astatus != Appoint.Status.CANCELED: # 状态异常,本该不存在这个任务 utils.operation_writer( global_info.system_log, f"预约{str(Aid)}的状态异常: {appoint.get_status()}", "web_func.startAppoint", "Error")
def addAppoint(contents): # 添加预约, main function # 首先检查房间是否存在 try: room = Room.objects.get(Rid=contents['Rid']) assert room.Rstatus == Room.Status.PERMITTED, 'room service suspended!' except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '房间不存在或当前房间暂停预约服务,请更换房间!', 'detail': str(e) } }, status=400) # 再检查学号对不对 students_id = contents['students'] # 存下学号列表 students = Student.objects.filter( Sid__in=students_id).distinct() # 获取学生objects try: assert len(students) == len( students_id), "students repeat or don't exists" except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '预约人信息有误,请检查后重新发起预约!', 'detail': str(e) } }, status=400) # 检查人员信息 try: #assert len(students) >= room.Rmin, f'at least {room.Rmin} students' real_min = room.Rmin if datetime.now().date( ) != contents['Astart'].date() else min(global_info.today_min, room.Rmin) assert len(students) + contents[ 'non_yp_num'] >= real_min, f'at least {room.Rmin} students' except Exception as e: return JsonResponse( {'statusInfo': { 'message': '使用总人数需达到房间最小人数!', 'detail': str(e) }}, status=400) # 检查外院人数是否过多 try: # assert len( # students) >= contents['non_yp_num'], f"too much non-yp students!" assert 2 * len( students) >= real_min, f"too little yp students!" except Exception as e: return JsonResponse( {'statusInfo': { # 'message': '外院人数不得超过总人数的一半!', 'message': '院内使用人数需要达到房间最小人数的一半!', 'detail': str(e) }}, status=400) # 检查如果是俄文楼,是否只有一个人使用 if "R" in room.Rid: # 如果是俄文楼系列 try: assert len( students) + contents['non_yp_num'] == 1, f"too many people using russian room!" except Exception as e: return JsonResponse( {'statusInfo': { 'message': '俄文楼元创空间仅支持单人预约!', 'detail': str(e) }}, status=400) # 检查预约时间是否正确 try: #Astart = datetime.strptime(contents['Astart'], '%Y-%m-%d %H:%M:%S') #Afinish = datetime.strptime(contents['Afinish'], '%Y-%m-%d %H:%M:%S') Astart = contents['Astart'] Afinish = contents['Afinish'] assert Astart <= Afinish, 'Appoint time error' assert Astart > datetime.now(), 'Appoint time error' except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '非法预约时间段,请不要擅自修改url!', 'detail': str(e) } }, status=400) # 预约是否超过3小时 try: assert Afinish <= Astart + timedelta(hours=3) except: return JsonResponse({'statusInfo': { 'message': '预约时常不能超过3小时!', }}, status=400) # 学号对了,人对了,房间是真实存在的,那就开始预约了 # 接下来开始搜索数据库,上锁 try: with transaction.atomic(): # 等待确认的和结束的肯定是当下时刻已经弄完的,所以不用管 print("得到搜索列表") appoints = room.appoint_list.select_for_update().exclude( Astatus=Appoint.Status.CANCELED).filter( Room_id=contents['Rid']) for appoint in appoints: start = appoint.Astart finish = appoint.Afinish # 第一种可能,开始在开始之前,只要结束的比开始晚就不行 # 第二种可能,开始在开始之后,只要在结束之前就都不行 if (start <= Astart < finish) or (Astart <= start < Afinish): # 有预约冲突的嫌疑,但要检查一下是不是重复预约了 if start == Astart and finish == Afinish and appoint.Ausage == contents['Ausage'] \ and appoint.Aannouncement == contents['announcement'] and appoint.Ayp_num == len(students) \ and appoint.Anon_yp_num == contents['non_yp_num'] and contents['Sid'] == appoint.major_student_id: # Room不用检查,肯定是同一个房间 utils.operation_writer( major_student.Sid, "重复发起同时段预约,预约号"+str(appoint.Aid), "func[addAppoint]", "OK") return JsonResponse({'data': appoint.toJson()}, status=200) else: # 预约冲突 return JsonResponse( { 'statusInfo': { 'message': '预约时间与已有预约冲突,请重选时间段!', 'detail': appoint.toJson() } }, status=400) # 获取预约发起者,确认预约状态 try: major_student = Student.objects.get(Sid=contents['Sid']) except: return JsonResponse( { 'statusInfo': { 'message': '发起人信息与登录信息不符,请不要在同一浏览器同时登录不同账号!', } }, status=400) # 确认信用分符合要求 try: assert major_student.Scredit > 0 except: return JsonResponse( {'statusInfo': { 'message': '信用分不足,本月无法发起预约!', }}, status=400) # 合法,可以返回了 appoint = Appoint(Room=room, Astart=Astart, Afinish=Afinish, Ausage=contents['Ausage'], Aannouncement=contents['announcement'], major_student=major_student, Anon_yp_num=contents['non_yp_num'], Ayp_num=len(students)) appoint.save() for student in students: appoint.students.add(student) appoint.save() # write by cdf start2 # 添加定时任务:finish scheduler.add_job(web_func.finishFunction, args=[appoint.Aid], id=f'{appoint.Aid}_finish', next_run_time=Afinish) # - timedelta(minutes=45)) # write by cdf end2 if datetime.now() <= appoint.Astart - timedelta(minutes=15): # 距离预约开始还有15分钟以上,提醒有新预约&定时任务 print('距离预约开始还有15分钟以上,提醒有新预约&定时任务', contents['new_require']) if contents['new_require'] == 1: # 只有在非长线预约中才添加这个job scheduler.add_job(utils.send_wechat_message, args=[students_id, appoint.Astart, appoint.Room, "new", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Anon_yp_num+appoint.Ayp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_new_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) scheduler.add_job(utils.send_wechat_message, args=[students_id, appoint.Astart, appoint.Room, "start", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Ayp_num+appoint.Anon_yp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_start_wechat', next_run_time=appoint.Astart - timedelta(minutes=15)) else: # 距离预约开始还有不到15分钟,提醒有新预约并且马上开始 # send_status, err_message = utils.send_wechat_message(students_id, appoint.Astart, appoint.Room,"new&start") scheduler.add_job(utils.send_wechat_message, args=[students_id, appoint.Astart, appoint.Room, "new&start", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Anon_yp_num+appoint.Ayp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_new_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) utils.operation_writer(major_student.Sid, "发起预约,预约号" + str(appoint.Aid), "func[addAppoint]", "OK") except Exception as e: utils.operation_writer(global_info.system_log, "学生" + str(major_student) + "出现添加预约失败的问题:"+str(e), "func[addAppoint]", "Error") return JsonResponse({'statusInfo': { 'message': '添加预约失败!请与管理员联系!', }}, status=400) return JsonResponse({'data': appoint.toJson()}, status=200)
def door_check(request): # 先以Sid Rid作为参数,看之后怎么改 get_post = request.get_full_path().split("?")[1].split("&") get_post = {i.split("=")[0]: i.split("=")[1] for i in get_post} # 获取房间基本信息,如果是自习室就开门 try: Sid, Rid = get_post['Sid'], get_post['Rid'] assert Sid is not None assert Rid is not None Rid = doortoroom(Rid) all_room = Room.objects.all() all_rid = [room.Rid for room in all_room] if Rid[:4] in all_rid: # 表示增加了一个未知的A\B号 Rid = Rid[:4] if Rid in all_rid: # 如果在房间列表里,考虑类型 if Room.objects.get( Rid=Rid).Rstatus == Room.Status.SUSPENDED: # 自习室 return JsonResponse({ "code": 0, "openDoor": "true" }, status=200) # 否则是预约房,进入后续逻辑 else: # 不在房间列表 raise SystemError student = Student.objects.get(Sid=Sid) except Exception as e: return JsonResponse({ "code": 1, "openDoor": "false", }, status=400) # 检查预约者和房间是否匹配 contents = {'Sid': str(Sid), 'kind': 'today'} stu_appoint = student.appoint_list.not_canceled() # 获取预约者今天的全部预约 stu_appoint = [ appoint for appoint in stu_appoint if appoint.Room_id == Rid and appoint.Astart.date() == datetime.now( ).date() and datetime.now() >= appoint.Astart - timedelta(minutes=15) and datetime.now() <= appoint.Afinish + timedelta(minutes=15) ] # 是这个房间and是今天的预约and在可开门时间范围内 if len(stu_appoint) == 0: # 没有预约,或不在开门时间范围内 return JsonResponse({ "code": 1, "openDoor": "false", }, status=400) else: # 到这里的一定是可以开门的 ''' # check the camera journal = open("journal.txt","a") journal.write(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) # journal.write('\t'+room.Rid+'\t') journal.write("开门\n") journal.close() ''' try: with transaction.atomic(): for now_appoint in stu_appoint: if (now_appoint.Astatus == Appoint.Status.APPOINTED and datetime.now() <= now_appoint.Astart + timedelta(minutes=15)): now_appoint.Astatus = Appoint.Status.PROCESSING now_appoint.save() except Exception as e: operation_writer( global_info.system_log, "可以开门却不开门的致命错误,房间号为" + str(Rid) + ",学生为" + str(Sid) + ",错误为:" + str(e), "func[doorcheck]", "Error") return JsonResponse( # 未知错误 { "code": 1, "openDoor": "false", }, status=400) return JsonResponse({"code": 0, "openDoor": "true"}, status=200)
def cameracheck(request): # 摄像头post的后端函数 # 获取摄像头信号,得到rid,最小人数 try: ip = request.META.get("REMOTE_ADDR") temp_stu_num = int( eval(request.body.decode('unicode-escape'))['body']['people_num']) rid = iptoroom(ip.split(".")[3]) # !!!!! # rid = 'B221' # just for debug room = Room.objects.get(Rid=rid) # 获取摄像头信号 num_need = room.Rmin # 最小房间人数 except: return JsonResponse({'statusInfo': { 'message': '缺少摄像头信息!', }}, status=400) now_time = datetime.now() # 更新现在的人数、最近更新时间 try: with transaction.atomic(): room.Rpresent = temp_stu_num room.Rlatest_time = now_time room.save() except Exception as e: operation_writer(global_info.system_log, "房间" + str(rid) + "更新摄像头人数失败1: " + str(e), "func[cameracheck]", "Error") return JsonResponse({'statusInfo': { 'message': '更新摄像头人数失败!', }}, status=400) # 检查时间问题,可能修改预约状态; appointments = Appoint.objects.not_canceled().filter( Q(Astart__lte=now_time) & Q(Afinish__gte=now_time) & Q(Room_id=rid)) # 只选取状态在1,2之间的预约 if len(appointments): # 如果有,只能有一个预约 content = appointments[0] if content.Atime.date() == content.Astart.date(): # 如果预约时间在使用时间的24h之内 则人数下限为2 num_need = min(global_info.today_min, num_need) try: if room.Rid in {"B109A", "B207"}: # 康德报告厅&小舞台 不考虑违约 content.Astatus = Appoint.Status.CONFIRMED content.save() else: # 其他房间 # added by wxy # 检查人数:采样、判断、更新 # 人数在finishappoint中检查 rand = random.uniform(0, 1) camera_lock.acquire() with transaction.atomic(): if rand > 1 - global_info.check_rate: content.Acamera_check_num += 1 if temp_stu_num >= num_need: content.Acamera_ok_num += 1 content.save() camera_lock.release() # add end except Exception as e: operation_writer(global_info.system_log, "预约" + str(content.Aid) + "更新摄像头人数失败2: " + str(e), "func[cameracheck]", "Error") return JsonResponse({'statusInfo': { 'message': '更新预约状态失败!', }}, status=400) try: if now_time > content.Astart + timedelta( minutes=15) and content.Astatus == Appoint.Status.APPOINTED: # added by wxy: 违约原因:迟到 status, tempmessage = appoint_violate(content, Appoint.Reason.R_LATE) if not status: operation_writer( global_info.system_log, "预约" + str(content.Aid) + "因迟到而违约,返回值出现异常: " + tempmessage, "func[cameracheck]", "Error") except Exception as e: operation_writer( global_info.system_log, "预约" + str(content.Aid) + "在迟到违约过程中: " + tempmessage, "func[cameracheck]", "Error") return JsonResponse({}, status=200) # 返回空就好 else: # 否则的话 相当于没有预约 正常返回 return JsonResponse({}, status=200) # 返回空就好
def addAppoint(contents): # 添加预约, main function # 检查是否为临时预约 add by lhw (2021.7.13) if 'Atemp_flag' not in contents.keys(): contents['Atemp_flag'] = False # 首先检查房间是否存在 try: room = Room.objects.get(Rid=contents['Rid']) assert room.Rstatus == Room.Status.PERMITTED, 'room service suspended!' except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '房间不存在或当前房间暂停预约服务,请更换房间!', 'detail': str(e) } }, status=400) # 再检查学号对不对 students_id = contents['students'] # 存下学号列表 students = Student.objects.filter( Sid__in=students_id).distinct() # 获取学生objects try: assert len(students) == len( students_id), "students repeat or don't exists" except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '预约人信息有误,请检查后重新发起预约!', 'detail': str(e) } }, status=400) # 检查人员信息 try: #assert len(students) >= room.Rmin, f'at least {room.Rmin} students' # ---- modify by lhw: 加入考虑临时预约的情况 ---- # current_time = datetime.now() # 获取当前时间,只获取一次,防止多次获取得到不同时间 if current_time.date() != contents['Astart'].date(): # 若不为当天 real_min = room.Rmin elif contents['Atemp_flag'] == False: # 当天预约,放宽限制 real_min = min(room.Rmin, global_info.today_min) else: # 临时预约,放宽限制 real_min = min(room.Rmin, global_info.temporary_min) # ----- modify end : 2021.7.10 ----- # assert len(students) + contents[ 'non_yp_num'] >= real_min, f'at least {room.Rmin} students' except Exception as e: return JsonResponse( {'statusInfo': { 'message': '使用总人数需达到房间最小人数!', 'detail': str(e) }}, status=400) # 检查外院人数是否过多 try: # assert len( # students) >= contents['non_yp_num'], f"too much non-yp students!" assert 2 * len(students) >= real_min, f"too little yp students!" except Exception as e: return JsonResponse( { 'statusInfo': { # 'message': '外院人数不得超过总人数的一半!', 'message': '院内使用人数需要达到房间最小人数的一半!', 'detail': str(e) } }, status=400) # 检查如果是俄文楼,是否只有一个人使用 if "R" in room.Rid: # 如果是俄文楼系列 try: assert len(students) + contents[ 'non_yp_num'] == 1, f"too many people using russian room!" except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '俄文楼元创空间仅支持单人预约!', 'detail': str(e) } }, status=400) # 检查预约时间是否正确 try: #Astart = datetime.strptime(contents['Astart'], '%Y-%m-%d %H:%M:%S') #Afinish = datetime.strptime(contents['Afinish'], '%Y-%m-%d %H:%M:%S') Astart = contents['Astart'] Afinish = contents['Afinish'] assert Astart <= Afinish, 'Appoint time error' # --- modify by lhw: Astart 可能比datetime.now小 --- # #assert Astart > datetime.now(), 'Appoint time error' assert Afinish > datetime.now(), 'Appoint time error' # --- modify end: 2021.7.10 --- # except Exception as e: return JsonResponse( { 'statusInfo': { 'message': '非法预约时间段,请不要擅自修改url!', 'detail': str(e) } }, status=400) # 预约是否超过3小时 try: assert Afinish <= Astart + timedelta(hours=3) except: return JsonResponse({'statusInfo': { 'message': '预约时常不能超过3小时!', }}, status=400) # 学号对了,人对了,房间是真实存在的,那就开始预约了 # 接下来开始搜索数据库,上锁 major_student = None # 避免下面未声明出错 try: with transaction.atomic(): # 获取预约发起者,确认预约状态 try: major_student = Student.objects.get(Sid=contents['Sid']) except: return JsonResponse( { 'statusInfo': { 'message': '发起人信息与登录信息不符,请不要在同一浏览器同时登录不同账号!', } }, status=400) # 等待确认的和结束的肯定是当下时刻已经弄完的,所以不用管 print("得到搜索列表") appoints = room.appoint_list.select_for_update().exclude( Astatus=Appoint.Status.CANCELED).filter( Room_id=contents['Rid']) for appoint in appoints: start = appoint.Astart finish = appoint.Afinish # 第一种可能,开始在开始之前,只要结束的比开始晚就不行 # 第二种可能,开始在开始之后,只要在结束之前就都不行 if (start <= Astart < finish) or (Astart <= start < Afinish): # 有预约冲突的嫌疑,但要检查一下是不是重复预约了 if start == Astart and finish == Afinish and appoint.Ausage == contents['Ausage'] \ and appoint.Aannouncement == contents['announcement'] and appoint.Ayp_num == len(students) \ and appoint.Anon_yp_num == contents['non_yp_num'] and contents['Sid'] == appoint.major_student_id: # Room不用检查,肯定是同一个房间 utils.operation_writer( major_student.Sid, "重复发起同时段预约,预约号" + str(appoint.Aid), "scheduler_func.addAppoint", "OK") return JsonResponse({'data': appoint.toJson()}, status=200) else: # 预约冲突 return JsonResponse( { 'statusInfo': { 'message': '预约时间与已有预约冲突,请重选时间段!', 'detail': appoint.toJson() } }, status=400) # 确认信用分符合要求 try: assert major_student.Scredit > 0 except: return JsonResponse( {'statusInfo': { 'message': '信用分不足,本月无法发起预约!', }}, status=400) # 合法,可以返回了 appoint = Appoint(Room=room, Astart=Astart, Afinish=Afinish, Ausage=contents['Ausage'], Aannouncement=contents['announcement'], major_student=major_student, Anon_yp_num=contents['non_yp_num'], Ayp_num=len(students), Atemp_flag=contents['Atemp_flag']) appoint.save() for student in students: appoint.students.add(student) appoint.save() # modify by pht: 整合定时任务为函数 set_scheduler(appoint) set_start_wechat(appoint, students_id=students_id, notify_new=bool(contents.get('new_require', True))) utils.operation_writer(major_student.Sid, "发起预约,预约号" + str(appoint.Aid), "scheduler_func.addAppoint", "OK") except Exception as e: utils.operation_writer( global_info.system_log, "学生" + str(major_student) + "出现添加预约失败的问题:" + str(e), "scheduler_func.addAppoint", "Error") return JsonResponse({'statusInfo': { 'message': '添加预约失败!请与管理员联系!', }}, status=400) return JsonResponse({'data': appoint.toJson()}, status=200)
def set_start_wechat(appoint, students_id=None, notify_new=True): '''将预约成功和开始前的提醒定时发送给微信''' if students_id is None: students_id = list(appoint.students.all().values_list('Sid', flat=True)) # write by cdf end2 # modify by pht: 如果已经开始,非临时预约记录log if datetime.now() >= appoint.Astart: # add by lhw : 临时预约 # if appoint.Atemp_flag == True: scheduler.add_job( utils.send_wechat_message, args=[ students_id, appoint.Astart, appoint.Room, "temp_appointment", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Anon_yp_num + appoint.Ayp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_start_wechat', replace_existing=True, next_run_time=datetime.now() + timedelta(seconds=5)) else: utils.operation_writer( global_info.system_log, "预约" + str(appoint.Aid) + "尝试发送给微信时已经开始,且并非临时预约", 'scheduler_func.set_start_wechat', "Problem") return False elif datetime.now() <= appoint.Astart - timedelta( minutes=15): # 距离预约开始还有15分钟以上,提醒有新预约&定时任务 # print('距离预约开始还有15分钟以上,提醒有新预约&定时任务', notify_new) if notify_new: # 只有在非长线预约中才添加这个job scheduler.add_job( utils.send_wechat_message, args=[ students_id, appoint.Astart, appoint.Room, "new", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Anon_yp_num + appoint.Ayp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_new_wechat', replace_existing=True, next_run_time=datetime.now() + timedelta(seconds=5)) scheduler.add_job( utils.send_wechat_message, args=[ students_id, appoint.Astart, appoint.Room, "start", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Ayp_num + appoint.Anon_yp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_start_wechat', replace_existing=True, next_run_time=appoint.Astart - timedelta(minutes=15)) else: # 距离预约开始还有不到15分钟,提醒有新预约并且马上开始 # send_status, err_message = utils.send_wechat_message(students_id, appoint.Astart, appoint.Room,"new&start") scheduler.add_job( utils.send_wechat_message, args=[ students_id, appoint.Astart, appoint.Room, "new&start", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Anon_yp_num + appoint.Ayp_num, '', # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_start_wechat', replace_existing=True, next_run_time=datetime.now() + timedelta(seconds=5)) return True
def index(request): # 主页 search_code = 0 warn_code = 0 message_code = 0 login_url = global_info.login_url # 处理学院公告 if (College_Announcement.objects.all()): try: message_item = College_Announcement.objects.get( show=College_Announcement.Show_Status.Yes) show_message = message_item.announcement message_code = 1 # 必定只有一个才能继续 except: message_code = 0 # print("无法顺利呈现公告,原因可能是没有将状态设置为YES或者超过一条状态被设置为YES") # 用户校验 if global_info.account_auth: # print("check", identity_check(request)) if not identity_check(request): try: if request.method == "GET": stu_id_ming = request.GET['Sid'] stu_id_code = request.GET['Secret'] timeStamp = request.GET['timeStamp'] request.session['Sid'] = stu_id_ming request.session['Secret'] = stu_id_code request.session['timeStamp'] = timeStamp assert identity_check(request) is True else: # POST 说明是display的修改,但是没登陆,自动错误 raise SystemError except: return redirect(direct_to_login(request)) # 至此获得了登录的授权 但是这个人可能不存在 加判断 try: request.session['Sname'] = Student.objects.get( Sid=request.session['Sid']).Sname # modify by pht: 自动更新姓名 if request.session['Sname'] == '未命名' and request.GET.get( 'name'): # 获取姓名和首字母 given_name = request.GET['name'] pinyin_list = pypinyin.pinyin(given_name, style=pypinyin.NORMAL) szm = ''.join([w[0][0] for w in pinyin_list]) # 更新数据库和session with transaction.atomic(): Student.objects.select_for_update().filter( Sid=request.session['Sid']).update( Sname=given_name, pinyin=szm) request.session['Sname'] = given_name except: # 没有这个人 自动添加并提示 if global_info.allow_newstu_appoint: with transaction.atomic(): success = 1 try: given_name = request.GET['name'] except: operation_writer( global_info.system_log, f"创建未命名用户:学号为{request.session['Sid']}", "views.index", "Problem") given_name = "未命名" success = 0 # 设置首字母 pinyin_list = pypinyin.pinyin(given_name, style=pypinyin.NORMAL) szm = ''.join([w[0][0] for w in pinyin_list]) student = Student(Sid=request.session['Sid'], Sname=given_name, Scredit=3, superuser=0, pinyin=szm) student.save() request.session['Sname'] = given_name warn_code = 1 if success == 1: warn_message = "数据库不存在学生信息,已为您自动创建!" else: warn_message = "数据库不存在学生信息,已为您自动创建,请联系管理员修改您的姓名!" else: # 学生不存在 request.session['Sid'] = "0000000000" request.session['Secret'] = "" # 清空信息 # request.session['Sname'] = Student.objects.get( # Sid=request.session['Sid']).Sname warn_code = 1 warn_message = "数据库不存在学生信息,请联系管理员添加!在此之前,您只能查看实时人数." else: request.session['Sid'] = global_info.debug_stuid request.session['Sname'] = Student.objects.get( Sid=request.session['Sid']).Sname #--------- 前端变量 ---------# room_list = Room.objects.all() now, tomorrow = datetime.now(), datetime.today() + timedelta(days=1) occupied_rooms = Appoint.objects.not_canceled().filter( Astart__lte=now + timedelta(minutes=15), Afinish__gte=now).values('Room') # 接下来有预约的房间 future_appointments = Appoint.objects.not_canceled().filter( Astart__gte=now + timedelta(minutes=15), Astart__lt=tomorrow) # 接下来的预约 room_appointments = {room.Rid: None for room in room_list} for appointment in future_appointments: # 每个房间的预约 room_appointments[appointment.Room.Rid] = min( room_appointments[appointment.Room.Rid] or timedelta(1), appointment.Astart - now) def format(delta): # 格式化timedelta,隐去0h if delta is None: return None hour, rem = divmod(delta.seconds, 3600) return f"{rem // 60}min" if hour == 0 else f"{hour}h{rem // 60}min" #--------- 1,2 地下室状态部分 ---------# double_list = ['航模', '绘画', '书法'] function_room_list = room_list.exclude(Rid__icontains="R").filter( Rstatus=Room.Status.PERMITTED).filter( ~Q(Rtitle__icontains="研讨") | Q(Rtitle__icontains="绘画") | Q(Rtitle__icontains="航模") | Q(Rtitle__icontains="书法")).order_by('Rid') #--------- 地下室状态:left tab ---------# suspended_room_list = room_list.filter( Rstatus=Room.Status.SUSPENDED).order_by('-Rtitle') # 开放房间 statistics_info = [(room, (room.Rpresent * 10) // (room.Rmax or 1)) for room in suspended_room_list] # 开放房间人数统计 #--------- 地下室状态:right tab ---------# talk_room_list = room_list.filter( # 研讨室(展示临时预约) Rtitle__icontains="研讨").filter(Rstatus=Room.Status.PERMITTED).order_by( 'Rmin', 'Rid') room_info = [ ( room, { 'Room': room.Rid } in occupied_rooms, format( # 研讨室占用情况 room_appointments[room.Rid])) for room in talk_room_list ] #--------- 3 俄文楼部分 ---------# russian_room_list = room_list.filter( Rstatus=Room.Status.PERMITTED).filter( # 俄文楼 Rid__icontains="R").order_by('Rid') russ_len = len(russian_room_list) if request.method == "POST": # YHT: added for Russian search request_time = request.POST.get("request_time", None) russ_request_time = request.POST.get("russ_request_time", None) check_type = "" if request_time is None and russ_request_time is not None: check_type = "russ" request_time = russ_request_time elif request_time is not None and russ_request_time is None: check_type = "talk" else: return render(request, 'Appointment/index.html', locals()) if request_time != None and request_time != "": # 初始加载或者不选时间直接搜索则直接返回index页面,否则进行如下反查时间 day, month, year = int(request_time[:2]), int( request_time[3:5]), int(request_time[6:10]) re_time = datetime(year, month, day) # 获取目前request时间的datetime结构 if re_time.date() < datetime.now().date(): # 如果搜过去时间 search_code = 1 search_message = "请不要搜索已经过去的时间!" return render(request, 'Appointment/index.html', locals()) elif re_time.date() - datetime.now().date() > timedelta(days=6): # 查看了7天之后的 search_code = 1 search_message = "只能查看最近7天的情况!" return render(request, 'Appointment/index.html', locals()) # 到这里 搜索没问题 进行跳转 urls = reverse("Appointment:arrange_talk") + "?year=" + str( year) + "&month=" + str(month) + "&day=" + str( day) + "&type=" + check_type # YHT: added for Russian search return redirect(urls) return render(request, 'Appointment/index.html', locals())
def confirm(self, request, queryset): # 确认通过 if not request.user.is_superuser: return self.message_user(request=request, message='操作失败,没有权限,请联系老师!', level=messages.WARNING) some_invalid = 0 have_success = 0 try: with transaction.atomic(): for appoint in queryset: if appoint.Astatus == Appoint.Status.WAITING: appoint.Astatus = Appoint.Status.CONFIRMED appoint.save() have_success = 1 # send wechat message scheduler.add_job( send_wechat_message, args=[ [appoint.major_student.Sid], # stuid_list appoint.Astart, # start_time appoint.Room, # room "confirm_admin_w2c", # message_type appoint.major_student.Sname, # major_student appoint.Ausage, # usage appoint.Aannouncement, appoint.Ayp_num + appoint.Anon_yp_num, appoint.get_status(), # reason # appoint.major_student.Scredit, ], id=f'{appoint.Aid}_confirm_admin_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) # 5s足够了 operation_writer( global_info.system_log, str(appoint.Aid) + "号预约被管理员从WAITING改为CONFIRMED" + "发起人:" + str(appoint.major_student), "admin.confirm", "OK") elif appoint.Astatus == Appoint.Status.VIOLATED: appoint.Astatus = Appoint.Status.JUDGED # for stu in appoint.students.all(): if appoint.major_student.Scredit < 3: appoint.major_student.Scredit += 1 appoint.major_student.save() appoint.save() have_success = 1 # send wechat message scheduler.add_job( send_wechat_message, args=[ [appoint.major_student.Sid], # stuid_list appoint.Astart, # start_time appoint.Room, # room "confirm_admin_v2j", # message_type appoint.major_student.Sname, # major_student appoint.Ausage, # usage appoint.Aannouncement, appoint.Ayp_num + appoint.Anon_yp_num, appoint.get_status(), # reason #appoint.major_student.Scredit, ], id=f'{appoint.Aid}_confirm_admin_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) # 5s足够了 operation_writer( global_info.system_log, str(appoint.Aid) + "号预约被管理员从VIOLATED改为JUDGED" + "发起人:" + str(appoint.major_student), "admin.confirm", "OK") else: # 不允许更改 some_invalid = 1 except: return self.message_user(request=request, message='操作失败!请与开发者联系!', level=messages.WARNING) if not some_invalid: return self.message_user(request, "更改状态成功!") else: if have_success: return self.message_user( request=request, message='部分修改成功!但遭遇状态不为等待、违约的预约,这部分预约不允许更改!', level=messages.WARNING) else: return self.message_user(request=request, message='修改失败!不允许修改状态不为等待、违约的预约!', level=messages.WARNING)
def finishAppoint(Aid): # 结束预约时的定时程序 ''' 结束预约时的定时程序 - 接受单个预约id - 可以处理任何状态的预约 - 对于非终止状态,判断人数是否合格,并转化为终止状态 要注意的是,由于定时任务可能执行多次,第二次的时候可能已经终止 ''' try: appoint = Appoint.objects.get(Aid=Aid) except: utils.operation_writer( global_info.system_log, f"预约{str(Aid)}意外消失", "web_func.finishAppoint", "Error") # 避免直接使用全局变量! by pht adjusted_camera_qualified_check_rate = global_info.camera_qualified_check_rate # --- add by pht: 终止状态 --- # TERMINATE_STATUSES = [ Appoint.Status.CONFIRMED, Appoint.Status.VIOLATED, Appoint.Status.CANCELED, ] # --- add by pht(2021.9.4) --- # # 如果处于非终止状态,只需检查人数判断是否合格 if appoint.Astatus not in TERMINATE_STATUSES: # 希望接受的非终止状态只有进行中,但其他状态也同样判定是否合格 if appoint.Astatus != Appoint.Status.PROCESSING: utils.operation_writer( appoint.major_student.Sid, f"预约{str(Aid)}结束时状态为{appoint.get_status()}:照常检查是否合格", "web_func.finishAppoint", "Error") # 摄像头出现超时问题,直接通过 if datetime.now() - appoint.Room.Rlatest_time > timedelta(minutes=15): appoint.Astatus = Appoint.Status.CONFIRMED # waiting appoint.save() utils.operation_writer( appoint.major_student.Sid, f"预约{str(Aid)}的状态变为{Appoint.Status.CONFIRMED}: 顺利完成", "web_func.finishAppoint", "OK") else: #if appoint.Acamera_check_num == 0: # utils.operation_writer( # global_info.system_log, f"预约{str(Aid)}的摄像头检测次数为零", "web_func.finishAppoint", "Error") # 检查人数是否足够 # added by pht: 需要根据状态调整 出于复用性和简洁性考虑在本函数前添加函数 # added by pht: 同时出于安全考虑 在本函数中重定义了本地rate 稍有修改 避免出错 adjusted_camera_qualified_check_rate = get_adjusted_qualified_rate( original_qualified_rate=adjusted_camera_qualified_check_rate, appoint=appoint ) if appoint.Acamera_ok_num < appoint.Acamera_check_num * adjusted_camera_qualified_check_rate - 0.01: # 人数不足 # add by lhw : 迟到的预约通知在这里处理。如果迟到不扣分,删掉这个if的内容即可,让下面那个camera check的if判断是否违规。 if appoint.Areason == Appoint.Reason.R_LATE: status, tempmessage = utils.appoint_violate( appoint, Appoint.Reason.R_LATE) if not status: utils.operation_writer( global_info.system_log, f"预约{str(Aid)}因迟到而违约时出现异常: {tempmessage}", "web_func.finishAppoint", "Error") else: status, tempmessage = utils.appoint_violate( appoint, Appoint.Reason.R_TOOLITTLE) if not status: utils.operation_writer( global_info.system_log, f"预约{str(Aid)}因人数不够而违约时出现异常: {tempmessage}", "web_func.finishAppoint", "Error") else: # 通过 appoint.Astatus = Appoint.Status.CONFIRMED appoint.save() utils.operation_writer( global_info.system_log, f"预约{str(Aid)}人数合格,已通过", "web_func.finishAppoint", "OK") else: if appoint.Astatus == Appoint.Status.CONFIRMED: # 可能已经判定通过,如公共区域和俄文楼 rid = appoint.Room.Rid if rid[:1] != 'R' and rid not in {'B109A', 'B207'}: utils.operation_writer( global_info.system_log, f"预约{str(Aid)}提前合格: {rid}房间", "web_func.finishAppoint", "Problem") elif appoint.Astatus != Appoint.Status.CANCELED: # 状态异常,多半是已经判定过了 utils.operation_writer( global_info.system_log, f"预约{str(Aid)}提前终止: {appoint.get_status()}", "web_func.finishAppoint", "Problem")
def finishFunction(Aid): # 结束预约时的定时程序 # 变更预约状态 appoint = Appoint.objects.get(Aid=Aid) mins15 = timedelta(minutes=15) # 避免直接使用全局变量! by pht adjusted_camera_qualified_check_rate = global_info.camera_qualified_check_rate try: # 如果处于进行中,表示没有迟到,只需检查人数 if appoint.Astatus == Appoint.Status.PROCESSING: # 摄像头出现超时问题,直接通过 if datetime.now() - appoint.Room.Rlatest_time > mins15: appoint.Astatus = Appoint.Status.CONFIRMED # waiting appoint.save() utils.operation_writer( appoint.major_student.Sid, "顺利完成预约" + str(appoint.Aid) + ",设为Confirm", "func[finishAppoint]", "OK") else: if appoint.Acamera_check_num == 0: utils.operation_writer( global_info.system_log, "预约" + str(appoint.Aid) + "摄像头检测次数为0", "finishAppoint", "Problem") # 检查人数是否足够 # added by pht: 需要根据状态调整 出于复用性和简洁性考虑在本函数前添加函数 # added by pht: 同时出于安全考虑 在本函数中重定义了本地rate 稍有修改 避免出错 adjusted_camera_qualified_check_rate = get_adjusted_qualified_rate( original_qualified_rate= adjusted_camera_qualified_check_rate, appoint=appoint, ) if appoint.Acamera_ok_num < appoint.Acamera_check_num * adjusted_camera_qualified_check_rate - 0.01: # 人数不足 status, tempmessage = utils.appoint_violate( appoint, Appoint.Reason.R_TOOLITTLE) if not status: utils.operation_writer( global_info.system_log, "预约" + str(appoint.Aid) + "因人数不够而违约时出现异常: " + tempmessage, "func[finishAppoint]", "Error") else: # 通过 appoint.Astatus = Appoint.Status.CONFIRMED appoint.save() # 表示压根没刷卡 elif appoint.Astatus == Appoint.Status.APPOINTED: # 特殊情况,不违约(地下室小舞台&康德,以及俄文楼) if (appoint.Room_id in {"B109A", "B207"}) or ('R' in appoint.Room_id): appoint.Astatus = Appoint.Status.CONFIRMED appoint.save() utils.operation_writer( appoint.major_student.Sid, "顺利完成预约" + str(appoint.Aid) + ",设为Confirm", "func[finishAppoint]", "OK") else: status, tempmessage = utils.appoint_violate( appoint, Appoint.Reason.R_LATE) if not status: utils.operation_writer( global_info.system_log, "预约" + str(appoint.Aid) + "因迟到而违约时出现异常: " + tempmessage, "func[finishAppoint]", "Error") # 如果上述过程出现不可预知的错误,记录 except Exception as e: utils.operation_writer( global_info.system_log, "预约" + str(appoint.Aid) + "在完成时出现异常:" + str(e) + ",提交为waiting状态,请处理!", "func[finishAppoint]", "Error") appoint.Astatus = Appoint.Status.WAITING # waiting appoint.save()
def longterm_wk(self, request, queryset, week_num): if not request.user.is_superuser: return self.message_user(request=request, message='操作失败,没有权限,请联系老师!', level=messages.WARNING) if len(queryset) != 1: return self.message_user(request=request, message='每次仅允许将一条预约长线化!', level=messages.WARNING) for appoint in queryset: # print(appoint) try: with transaction.atomic(): stuid_list = [stu.Sid for stu in appoint.students.all()] for i in range(week_num): # 调用函数完成预约 feedback = addAppoint({ 'Rid': appoint.Room.Rid, 'students': stuid_list, 'non_yp_num': appoint.Anon_yp_num, 'Astart': appoint.Astart + (i + 1) * timedelta(days=7), 'Afinish': appoint.Afinish + (i + 1) * timedelta(days=7), 'Sid': appoint.major_student.Sid, 'Ausage': appoint.Ausage, 'announcement': appoint.Aannouncement, 'new_require': # 长线预约,不需要每一个都添加信息, 直接统一添加 0 }) if feedback.status_code != 200: # 成功预约 warning = eval( feedback.content.decode( 'unicode-escape'))['statusInfo']['message'] print(warning) raise Exception(warning) ''' newappoint = Appoint( Room=appoint.Room, Astart=appoint.Astart + (i+1) * timedelta(days=7), Afinish=appoint.Afinish + \ (i+1) * timedelta(days=7), Ausage=appoint.Ausage, Aannouncement=appoint.Aannouncement, major_student=appoint.major_student, Anon_yp_num=appoint.Anon_yp_num, Ayp_num=appoint.Ayp_num ) newappoint.save() for tempstudent in appoint.students.all(): print(tempstudent) newappoint.students.add(tempstudent) newappoint.save() ''' except Exception as e: operation_writer( global_info.system_log, "学生" + str(appoint.major_student) + "出现添加长线化预约失败的问题:" + str(e), "admin.longterm", "Problem") return self.message_user(request=request, message=str(e), level=messages.WARNING) # 到这里, 长线化预约发起成功 scheduler.add_job( send_wechat_message, args=[ stuid_list, # stuid_list appoint.Astart, # start_time appoint.Room, # room "longterm", # message_type appoint.major_student.Sname, # major_student appoint.Ausage, # usage appoint.Aannouncement, len(stuid_list) + appoint.Anon_yp_num, week_num, # reason, 这里用作表示持续周数 #appoint.major_student.Scredit, ], id=f'{appoint.Aid}_new_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) # 2s足够了 operation_writer( appoint.major_student.Sid, "发起" + str(week_num) + "周的长线化预约, 原始预约号" + str(appoint.Aid), "admin.longterm", "OK") return self.message_user(request, '长线化成功!')
def cancelFunction(request): # 取消预约 warn_code = 0 try: Aid = request.POST.get('cancel_btn') appoints = Appoint.objects.filter(Astatus=Appoint.Status.APPOINTED) appoint = appoints.get(Aid=Aid) except: warn_code = 1 warning = "预约不存在、已经开始或者已取消!" # return render(request, 'Appointment/admin-index.html', locals()) return redirect( reverse("Appointment:admin_index") + "?warn_code=" + str(warn_code) + "&warning=" + warning) try: assert appoint.major_student.Sid == request.session['Sid'] except: warn_code = 1 warning = "请不要恶意尝试取消不是自己发起的预约!" # return render(request, 'Appointment/admin-index.html', locals()) return redirect( reverse("Appointment:admin_index") + "?warn_code=" + str(warn_code) + "&warning=" + warning) if appoint.Astart < datetime.now() + timedelta(minutes=30): warn_code = 1 warning = "不能取消开始时间在30分钟之内的预约!" return redirect( reverse("Appointment:admin_index") + "?warn_code=" + str(warn_code) + "&warning=" + warning) # 先准备发送人 stu_list = [stu.Sid for stu in appoint.students.all()] with transaction.atomic(): appoint_room_name = appoint.Room.Rtitle appoint.cancel() try: scheduler.remove_job(f'{appoint.Aid}_finish') except: utils.operation_writer(global_info.system_log, "预约"+str(appoint.Aid) + "取消时发现不存在计时器", 'func[cancelAppoint]', "Problem") utils.operation_writer(appoint.major_student.Sid, "取消了预约" + str(appoint.Aid), "func[cancelAppoint]", "OK") warn_code = 2 warning = "成功取消对" + appoint_room_name + "的预约!" # send_status, err_message = utils.send_wechat_message([appoint.major_student.Sid],appoint.Astart,appoint.Room,"cancel") # todo: to all print('will send cancel message') scheduler.add_job(utils.send_wechat_message, args=[stu_list, appoint.Astart, appoint.Room, "cancel", appoint.major_student.Sname, appoint.Ausage, appoint.Aannouncement, appoint.Anon_yp_num+appoint.Ayp_num, '', #appoint.major_student.Scredit, ], id=f'{appoint.Aid}_cancel_wechat', next_run_time=datetime.now() + timedelta(seconds=5)) ''' if send_status == 1: # 记录错误信息 utils.operation_writer(global_info.system_log, "预约" + str(appoint.Aid) + "取消时向微信发消息失败,原因:"+err_message, "func[addAppoint]", "Problem") ''' # cancel wechat scheduler try: scheduler.remove_job(f'{appoint.Aid}_start_wechat') except: utils.operation_writer(global_info.system_log, "预约"+str(appoint.Aid) + "取消时发现不存在wechat计时器,但也可能本来就没有", 'func[cancelAppoint]', "Problem") return redirect( reverse("Appointment:admin_index") + "?warn_code=" + str(warn_code) + "&warning=" + warning)
def cameracheck(request): # 摄像头post的后端函数 # 获取摄像头信号,得到rid,最小人数 try: ip = request.META.get("REMOTE_ADDR") temp_stu_num = int( eval(request.body.decode('unicode-escape'))['body']['people_num']) rid = iptoroom(ip.split(".")[3]) # !!!!! # rid = 'B221' # just for debug room = Room.objects.get(Rid=rid) # 获取摄像头信号 num_need = room.Rmin # 最小房间人数 except: return JsonResponse({'statusInfo': { 'message': '缺少摄像头信息!', }}, status=400) now_time = datetime.now() # 存储上一次的检测时间 room_previous_check_time = room.Rlatest_time # 更新现在的人数、最近更新时间 try: with transaction.atomic(): room.Rpresent = temp_stu_num room.Rlatest_time = now_time room.save() except Exception as e: operation_writer(global_info.system_log, "房间" + str(rid) + "更新摄像头人数失败1: " + str(e), "views.cameracheck", "Error") return JsonResponse({'statusInfo': { 'message': '更新摄像头人数失败!', }}, status=400) # 检查时间问题,可能修改预约状态; appointments = Appoint.objects.not_canceled().filter( Q(Astart__lte=now_time) & Q(Afinish__gte=now_time) & Q(Room_id=rid)) # 只选取状态在1,2之间的预约 if len(appointments): # 如果有,只能有一个预约 content = appointments[0] if room.Rid == "B107B": # 107b的监控不太靠谱,正下方看不到 num_need = min(max(global_info.today_min, num_need - 2), num_need) elif room.Rid == "B217": # 地下室关灯导致判定不清晰,晚上更严重 if content.Astart.hour >= 20: num_need = min(max(global_info.today_min, num_need - 2), num_need) else: num_need = min(max(global_info.today_min, num_need - 1), num_need) if content.Atime.date() == content.Astart.date(): # 如果预约时间在使用时间的24h之内 则人数下限为2 num_need = min(global_info.today_min, num_need) if content.Atemp_flag == Appoint.Bool_flag.Yes: # 如果为临时预约 则人数下限为1 不作为合格标准 只是记录 num_need = min(global_info.temporary_min, num_need) try: if room.Rid in {"B109A", "B207"}: # 康德报告厅&小舞台 不考虑违约 content.Astatus = Appoint.Status.CONFIRMED content.save() else: # 其他房间 # added by wxy # 检查人数:采样、判断、更新 # 人数在finishappoint中检查 # modified by pht - 2021/8/15 # 增加UNSAVED状态 # 逻辑是尽量宽容,因为一分钟只记录两次,两次随机大概率只有一次成功 # 所以没必要必须随机成功才能修改错误结果 rand = random.uniform(0, 1) camera_lock.acquire() with transaction.atomic(): if now_time.minute != room_previous_check_time.minute or\ content.Acheck_status == Appoint.Check_status.UNSAVED: # 说明是新的一分钟或者本分钟还没有记录 # 如果随机成功,记录新的检查结果 if rand < global_info.check_rate: content.Acheck_status = Appoint.Check_status.FAILED content.Acamera_check_num += 1 if temp_stu_num >= num_need: # 如果本次检测合规 content.Acamera_ok_num += 1 content.Acheck_status = Appoint.Check_status.PASSED # 如果随机失败,锁定上一分钟的结果 else: if content.Acheck_status == Appoint.Check_status.FAILED: # 如果本次检测合规,宽容时也算上一次通过(因为一分钟只检测两次) if temp_stu_num >= num_need: content.Acamera_ok_num += 1 # 本分钟暂无记录 content.Acheck_status = Appoint.Check_status.UNSAVED else: # 和上一次检测在同一分钟,此时:1.不增加检测次数 2.如果合规则增加ok次数 if content.Acheck_status == Appoint.Check_status.FAILED: # 当前不合规;如果这次检测合规,那么认为本分钟合规 if temp_stu_num >= num_need: content.Acamera_ok_num += 1 content.Acheck_status = Appoint.Check_status.PASSED # else:当前已经合规,不需要额外操作 content.save() camera_lock.release() # add end except Exception as e: operation_writer(global_info.system_log, "预约" + str(content.Aid) + "更新摄像头人数失败2: " + str(e), "views.cameracheck", "Error") return JsonResponse({'statusInfo': { 'message': '更新预约状态失败!', }}, status=400) try: if now_time > content.Astart + timedelta( minutes=15) and content.Astatus == Appoint.Status.APPOINTED: status, tempmessage = set_appoint_reason( content, Appoint.Reason.R_LATE) # 该函数只是把appoint标记为迟到(填写reason)并修改状态为进行中,不发送微信提醒 if not status: operation_writer( global_info.system_log, "预约" + str(content.Aid) + "设置为迟到时的返回值异常 " + tempmessage, "views.cameracheck", "Error") except Exception as e: operation_writer( global_info.system_log, "预约" + str(content.Aid) + "在迟到状态设置过程中: " + tempmessage, "views.cameracheck", "Error") # added by wxy: 违约原因:迟到 # status, tempmessage = appoint_violate( # content, Appoint.Reason.R_LATE) # if not status: # operation_writer(global_info.system_log, "预约"+str(content.Aid) + # "因迟到而违约,返回值出现异常: "+tempmessage, "views.cameracheck", "Error") # except Exception as e: # operation_writer(global_info.system_log, "预约"+str(content.Aid) + # "在迟到违约过程中: "+tempmessage, "views.cameracheck", "Error") return JsonResponse({}, status=200) # 返回空就好 else: # 否则的话 相当于没有预约 正常返回 return JsonResponse({}, status=200) # 返回空就好